blob: f1b9052b9f6de90f636759ad2d74997f4eed1dcc [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
ossu7bb87ee2017-01-23 04:56:25 -080011#include "webrtc/pc/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/logging.h"
28#include "webrtc/base/messagedigest.h"
29#include "webrtc/base/stringutils.h"
isheriff6f8d6862016-05-26 11:24:55 -070030// for RtpExtension
31#include "webrtc/config.h"
kjellandera96e2d72016-02-04 23:52:28 -080032#include "webrtc/media/base/codec.h"
kjellandera96e2d72016-02-04 23:52:28 -080033#include "webrtc/media/base/cryptoparams.h"
kjellanderf4752772016-03-02 05:42:30 -080034#include "webrtc/media/base/mediaconstants.h"
kjellandera96e2d72016-02-04 23:52:28 -080035#include "webrtc/media/base/rtputils.h"
deadbeef953c2ce2017-01-09 14:53:41 -080036#include "webrtc/media/sctp/sctptransportinternal.h"
kjellandera96e2d72016-02-04 23:52:28 -080037#include "webrtc/p2p/base/candidate.h"
kjellanderf4752772016-03-02 05:42:30 -080038#include "webrtc/p2p/base/p2pconstants.h"
kjellandera96e2d72016-02-04 23:52:28 -080039#include "webrtc/p2p/base/port.h"
kjellander@webrtc.org9b8df252016-02-12 06:47:59 +010040#include "webrtc/pc/mediasession.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000041
42using cricket::AudioContentDescription;
43using cricket::Candidate;
44using cricket::Candidates;
45using cricket::ContentDescription;
46using cricket::ContentInfo;
47using cricket::CryptoParams;
48using cricket::DataContentDescription;
49using cricket::ICE_CANDIDATE_COMPONENT_RTP;
50using cricket::ICE_CANDIDATE_COMPONENT_RTCP;
51using cricket::kCodecParamMaxBitrate;
52using cricket::kCodecParamMaxPTime;
53using cricket::kCodecParamMaxQuantization;
54using cricket::kCodecParamMinBitrate;
55using cricket::kCodecParamMinPTime;
56using cricket::kCodecParamPTime;
57using cricket::kCodecParamSPropStereo;
buildbot@webrtc.orged97bb02014-05-07 11:15:20 +000058using cricket::kCodecParamStartBitrate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000059using cricket::kCodecParamStereo;
60using cricket::kCodecParamUseInbandFec;
Minyue Li7100dcd2015-03-27 05:05:59 +010061using cricket::kCodecParamUseDtx;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000062using cricket::kCodecParamSctpProtocol;
63using cricket::kCodecParamSctpStreams;
henrike@webrtc.org1e09a712013-07-26 19:17:59 +000064using cricket::kCodecParamMaxAverageBitrate;
buildbot@webrtc.org5d639b32014-09-10 07:57:12 +000065using cricket::kCodecParamMaxPlaybackRate;
stefan@webrtc.org85d27942014-06-09 12:51:39 +000066using cricket::kCodecParamAssociatedPayloadType;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000067using cricket::MediaContentDescription;
68using cricket::MediaType;
isheriff6f8d6862016-05-26 11:24:55 -070069using cricket::RtpHeaderExtensions;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000070using cricket::SsrcGroup;
71using cricket::StreamParams;
72using cricket::StreamParamsVec;
73using cricket::TransportDescription;
74using cricket::TransportInfo;
75using cricket::VideoContentDescription;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000076using rtc::SocketAddress;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000077
henrike@webrtc.org28e20752013-07-10 00:45:36 +000078namespace cricket {
79class SessionDescription;
80}
81
82namespace webrtc {
83
84// Line type
85// RFC 4566
86// An SDP session description consists of a number of lines of text of
87// the form:
88// <type>=<value>
89// where <type> MUST be exactly one case-significant character.
deadbeef9d3584c2016-02-16 17:54:10 -080090static const int kLinePrefixLength = 2; // Length of <type>=
henrike@webrtc.org28e20752013-07-10 00:45:36 +000091static const char kLineTypeVersion = 'v';
92static const char kLineTypeOrigin = 'o';
93static const char kLineTypeSessionName = 's';
94static const char kLineTypeSessionInfo = 'i';
95static const char kLineTypeSessionUri = 'u';
96static const char kLineTypeSessionEmail = 'e';
97static const char kLineTypeSessionPhone = 'p';
98static const char kLineTypeSessionBandwidth = 'b';
99static const char kLineTypeTiming = 't';
100static const char kLineTypeRepeatTimes = 'r';
101static const char kLineTypeTimeZone = 'z';
102static const char kLineTypeEncryptionKey = 'k';
103static const char kLineTypeMedia = 'm';
104static const char kLineTypeConnection = 'c';
105static const char kLineTypeAttributes = 'a';
106
107// Attributes
108static const char kAttributeGroup[] = "group";
109static const char kAttributeMid[] = "mid";
deadbeef9d3584c2016-02-16 17:54:10 -0800110static const char kAttributeMsid[] = "msid";
deadbeef25ed4352016-12-12 18:37:36 -0800111static const char kAttributeBundleOnly[] = "bundle-only";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000112static const char kAttributeRtcpMux[] = "rtcp-mux";
deadbeef13871492015-12-09 12:37:51 -0800113static const char kAttributeRtcpReducedSize[] = "rtcp-rsize";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000114static const char kAttributeSsrc[] = "ssrc";
115static const char kSsrcAttributeCname[] = "cname";
116static const char kAttributeExtmap[] = "extmap";
117// draft-alvestrand-mmusic-msid-01
118// a=msid-semantic: WMS
119static const char kAttributeMsidSemantics[] = "msid-semantic";
120static const char kMediaStreamSemantic[] = "WMS";
121static const char kSsrcAttributeMsid[] = "msid";
122static const char kDefaultMsid[] = "default";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000123static const char kSsrcAttributeMslabel[] = "mslabel";
124static const char kSSrcAttributeLabel[] = "label";
125static const char kAttributeSsrcGroup[] = "ssrc-group";
126static const char kAttributeCrypto[] = "crypto";
127static const char kAttributeCandidate[] = "candidate";
128static const char kAttributeCandidateTyp[] = "typ";
129static const char kAttributeCandidateRaddr[] = "raddr";
130static const char kAttributeCandidateRport[] = "rport";
honghaiza54a0802015-12-16 18:37:23 -0800131static const char kAttributeCandidateUfrag[] = "ufrag";
132static const char kAttributeCandidatePwd[] = "pwd";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000133static const char kAttributeCandidateGeneration[] = "generation";
honghaiza0c44ea2016-03-23 16:07:48 -0700134static const char kAttributeCandidateNetworkId[] = "network-id";
honghaize1a0c942016-02-16 14:54:56 -0800135static const char kAttributeCandidateNetworkCost[] = "network-cost";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000136static const char kAttributeFingerprint[] = "fingerprint";
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000137static const char kAttributeSetup[] = "setup";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000138static const char kAttributeFmtp[] = "fmtp";
139static const char kAttributeRtpmap[] = "rtpmap";
wu@webrtc.org78187522013-10-07 23:32:02 +0000140static const char kAttributeSctpmap[] = "sctpmap";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000141static const char kAttributeRtcp[] = "rtcp";
142static const char kAttributeIceUfrag[] = "ice-ufrag";
143static const char kAttributeIcePwd[] = "ice-pwd";
144static const char kAttributeIceLite[] = "ice-lite";
145static const char kAttributeIceOption[] = "ice-options";
146static const char kAttributeSendOnly[] = "sendonly";
147static const char kAttributeRecvOnly[] = "recvonly";
148static const char kAttributeRtcpFb[] = "rtcp-fb";
149static const char kAttributeSendRecv[] = "sendrecv";
150static const char kAttributeInactive[] = "inactive";
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +0000151// draft-ietf-mmusic-sctp-sdp-07
152// a=sctp-port
153static const char kAttributeSctpPort[] = "sctp-port";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000154
155// Experimental flags
156static const char kAttributeXGoogleFlag[] = "x-google-flag";
157static const char kValueConference[] = "conference";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000158
159// Candidate
160static const char kCandidateHost[] = "host";
161static const char kCandidateSrflx[] = "srflx";
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700162static const char kCandidatePrflx[] = "prflx";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000163static const char kCandidateRelay[] = "relay";
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +0000164static const char kTcpCandidateType[] = "tcptype";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000165
166static const char kSdpDelimiterEqual = '=';
167static const char kSdpDelimiterSpace = ' ';
168static const char kSdpDelimiterColon = ':';
169static const char kSdpDelimiterSemicolon = ';';
170static const char kSdpDelimiterSlash = '/';
171static const char kNewLine = '\n';
172static const char kReturn = '\r';
173static const char kLineBreak[] = "\r\n";
174
175// TODO: Generate the Session and Time description
176// instead of hardcoding.
177static const char kSessionVersion[] = "v=0";
178// RFC 4566
179static const char kSessionOriginUsername[] = "-";
180static const char kSessionOriginSessionId[] = "0";
181static const char kSessionOriginSessionVersion[] = "0";
182static const char kSessionOriginNettype[] = "IN";
183static const char kSessionOriginAddrtype[] = "IP4";
184static const char kSessionOriginAddress[] = "127.0.0.1";
185static const char kSessionName[] = "s=-";
186static const char kTimeDescription[] = "t=0 0";
187static const char kAttrGroup[] = "a=group:BUNDLE";
188static const char kConnectionNettype[] = "IN";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000189static const char kConnectionIpv4Addrtype[] = "IP4";
190static const char kConnectionIpv6Addrtype[] = "IP6";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000191static const char kMediaTypeVideo[] = "video";
192static const char kMediaTypeAudio[] = "audio";
193static const char kMediaTypeData[] = "application";
194static const char kMediaPortRejected[] = "0";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000195// draft-ietf-mmusic-trickle-ice-01
196// When no candidates have been gathered, set the connection
197// address to IP6 ::.
perkj@webrtc.orgd105cc82014-11-07 11:22:06 +0000198// TODO(perkj): FF can not parse IP6 ::. See http://crbug/430333
199// Use IPV4 per default.
200static const char kDummyAddress[] = "0.0.0.0";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000201static const char kDummyPort[] = "9";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000202// RFC 3556
203static const char kApplicationSpecificMaximum[] = "AS";
204
205static const int kDefaultVideoClockrate = 90000;
206
wu@webrtc.org78187522013-10-07 23:32:02 +0000207static const char kDefaultSctpmapProtocol[] = "webrtc-datachannel";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000208
pkasting@chromium.orgd3245462015-02-23 21:28:22 +0000209// RTP payload type is in the 0-127 range. Use -1 to indicate "all" payload
210// types.
211const int kWildcardPayloadType = -1;
212
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000213struct SsrcInfo {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200214 uint32_t ssrc_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000215 std::string cname;
deadbeef9d3584c2016-02-16 17:54:10 -0800216 std::string stream_id;
217 std::string track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000218
219 // For backward compatibility.
220 // TODO(ronghuawu): Remove below 2 fields once all the clients support msid.
221 std::string label;
222 std::string mslabel;
223};
224typedef std::vector<SsrcInfo> SsrcInfoVec;
225typedef std::vector<SsrcGroup> SsrcGroupVec;
226
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000227template <class T>
228static void AddFmtpLine(const T& codec, std::string* message);
229static void BuildMediaDescription(const ContentInfo* content_info,
230 const TransportInfo* transport_info,
231 const MediaType media_type,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000232 const std::vector<Candidate>& candidates,
deadbeef9d3584c2016-02-16 17:54:10 -0800233 bool unified_plan_sdp,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000234 std::string* message);
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000235static void BuildSctpContentAttributes(std::string* message, int sctp_port);
deadbeef9d3584c2016-02-16 17:54:10 -0800236static void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
237 const MediaType media_type,
238 bool unified_plan_sdp,
239 std::string* message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000240static void BuildRtpMap(const MediaContentDescription* media_desc,
241 const MediaType media_type,
242 std::string* message);
243static void BuildCandidate(const std::vector<Candidate>& candidates,
honghaiza54a0802015-12-16 18:37:23 -0800244 bool include_ufrag,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000245 std::string* message);
246static void BuildIceOptions(const std::vector<std::string>& transport_options,
247 std::string* message);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +0000248static bool IsRtp(const std::string& protocol);
249static bool IsDtlsSctp(const std::string& protocol);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000250static bool ParseSessionDescription(const std::string& message, size_t* pos,
251 std::string* session_id,
252 std::string* session_version,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000253 TransportDescription* session_td,
254 RtpHeaderExtensions* session_extmaps,
255 cricket::SessionDescription* desc,
256 SdpParseError* error);
257static bool ParseGroupAttribute(const std::string& line,
258 cricket::SessionDescription* desc,
259 SdpParseError* error);
260static bool ParseMediaDescription(
261 const std::string& message,
262 const TransportDescription& session_td,
263 const RtpHeaderExtensions& session_extmaps,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000264 size_t* pos, cricket::SessionDescription* desc,
265 std::vector<JsepIceCandidate*>* candidates,
266 SdpParseError* error);
267static bool ParseContent(const std::string& message,
268 const MediaType media_type,
269 int mline_index,
270 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -0700271 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000272 size_t* pos,
273 std::string* content_name,
deadbeef25ed4352016-12-12 18:37:36 -0800274 bool* bundle_only,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000275 MediaContentDescription* media_desc,
276 TransportDescription* transport,
277 std::vector<JsepIceCandidate*>* candidates,
278 SdpParseError* error);
279static bool ParseSsrcAttribute(const std::string& line,
280 SsrcInfoVec* ssrc_infos,
281 SdpParseError* error);
282static bool ParseSsrcGroupAttribute(const std::string& line,
283 SsrcGroupVec* ssrc_groups,
284 SdpParseError* error);
285static bool ParseCryptoAttribute(const std::string& line,
286 MediaContentDescription* media_desc,
287 SdpParseError* error);
288static bool ParseRtpmapAttribute(const std::string& line,
289 const MediaType media_type,
deadbeef67cf2c12016-04-13 10:07:16 -0700290 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000291 MediaContentDescription* media_desc,
292 SdpParseError* error);
293static bool ParseFmtpAttributes(const std::string& line,
294 const MediaType media_type,
295 MediaContentDescription* media_desc,
296 SdpParseError* error);
297static bool ParseFmtpParam(const std::string& line, std::string* parameter,
298 std::string* value, SdpParseError* error);
299static bool ParseCandidate(const std::string& message, Candidate* candidate,
300 SdpParseError* error, bool is_raw);
301static bool ParseRtcpFbAttribute(const std::string& line,
302 const MediaType media_type,
303 MediaContentDescription* media_desc,
304 SdpParseError* error);
305static bool ParseIceOptions(const std::string& line,
306 std::vector<std::string>* transport_options,
307 SdpParseError* error);
308static bool ParseExtmap(const std::string& line,
isheriff6f8d6862016-05-26 11:24:55 -0700309 RtpExtension* extmap,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000310 SdpParseError* error);
311static bool ParseFingerprintAttribute(const std::string& line,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000312 rtc::SSLFingerprint** fingerprint,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000313 SdpParseError* error);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000314static bool ParseDtlsSetup(const std::string& line,
315 cricket::ConnectionRole* role,
316 SdpParseError* error);
deadbeef9d3584c2016-02-16 17:54:10 -0800317static bool ParseMsidAttribute(const std::string& line,
318 std::string* stream_id,
319 std::string* track_id,
320 SdpParseError* error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000321
322// Helper functions
323
324// Below ParseFailed*** functions output the line that caused the parsing
325// failure and the detailed reason (|description|) of the failure to |error|.
326// The functions always return false so that they can be used directly in the
327// following way when error happens:
328// "return ParseFailed***(...);"
329
330// The line starting at |line_start| of |message| is the failing line.
331// The reason for the failure should be provided in the |description|.
332// An example of a description could be "unknown character".
333static bool ParseFailed(const std::string& message,
334 size_t line_start,
335 const std::string& description,
336 SdpParseError* error) {
337 // Get the first line of |message| from |line_start|.
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000338 std::string first_line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000339 size_t line_end = message.find(kNewLine, line_start);
340 if (line_end != std::string::npos) {
341 if (line_end > 0 && (message.at(line_end - 1) == kReturn)) {
342 --line_end;
343 }
344 first_line = message.substr(line_start, (line_end - line_start));
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000345 } else {
346 first_line = message.substr(line_start);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000347 }
348
349 if (error) {
350 error->line = first_line;
351 error->description = description;
352 }
353 LOG(LS_ERROR) << "Failed to parse: \"" << first_line
354 << "\". Reason: " << description;
355 return false;
356}
357
358// |line| is the failing line. The reason for the failure should be
359// provided in the |description|.
360static bool ParseFailed(const std::string& line,
361 const std::string& description,
362 SdpParseError* error) {
363 return ParseFailed(line, 0, description, error);
364}
365
366// Parses failure where the failing SDP line isn't know or there are multiple
367// failing lines.
368static bool ParseFailed(const std::string& description,
369 SdpParseError* error) {
370 return ParseFailed("", description, error);
371}
372
373// |line| is the failing line. The failure is due to the fact that |line|
374// doesn't have |expected_fields| fields.
375static bool ParseFailedExpectFieldNum(const std::string& line,
376 int expected_fields,
377 SdpParseError* error) {
378 std::ostringstream description;
379 description << "Expects " << expected_fields << " fields.";
380 return ParseFailed(line, description.str(), error);
381}
382
383// |line| is the failing line. The failure is due to the fact that |line| has
384// less than |expected_min_fields| fields.
385static bool ParseFailedExpectMinFieldNum(const std::string& line,
386 int expected_min_fields,
387 SdpParseError* error) {
388 std::ostringstream description;
389 description << "Expects at least " << expected_min_fields << " fields.";
390 return ParseFailed(line, description.str(), error);
391}
392
393// |line| is the failing line. The failure is due to the fact that it failed to
394// get the value of |attribute|.
395static bool ParseFailedGetValue(const std::string& line,
396 const std::string& attribute,
397 SdpParseError* error) {
398 std::ostringstream description;
399 description << "Failed to get the value of attribute: " << attribute;
400 return ParseFailed(line, description.str(), error);
401}
402
403// The line starting at |line_start| of |message| is the failing line. The
404// failure is due to the line type (e.g. the "m" part of the "m-line")
405// not matching what is expected. The expected line type should be
406// provided as |line_type|.
407static bool ParseFailedExpectLine(const std::string& message,
408 size_t line_start,
409 const char line_type,
410 const std::string& line_value,
411 SdpParseError* error) {
412 std::ostringstream description;
413 description << "Expect line: " << line_type << "=" << line_value;
414 return ParseFailed(message, line_start, description.str(), error);
415}
416
417static bool AddLine(const std::string& line, std::string* message) {
418 if (!message)
419 return false;
420
421 message->append(line);
422 message->append(kLineBreak);
423 return true;
424}
425
426static bool GetLine(const std::string& message,
427 size_t* pos,
428 std::string* line) {
429 size_t line_begin = *pos;
430 size_t line_end = message.find(kNewLine, line_begin);
431 if (line_end == std::string::npos) {
432 return false;
433 }
434 // Update the new start position
435 *pos = line_end + 1;
436 if (line_end > 0 && (message.at(line_end - 1) == kReturn)) {
437 --line_end;
438 }
439 *line = message.substr(line_begin, (line_end - line_begin));
440 const char* cline = line->c_str();
441 // RFC 4566
442 // An SDP session description consists of a number of lines of text of
443 // the form:
444 // <type>=<value>
445 // where <type> MUST be exactly one case-significant character and
446 // <value> is structured text whose format depends on <type>.
447 // Whitespace MUST NOT be used on either side of the "=" sign.
decurtis@webrtc.org8af11042015-01-07 19:15:51 +0000448 if (line->length() < 3 ||
449 !islower(cline[0]) ||
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000450 cline[1] != kSdpDelimiterEqual ||
451 cline[2] == kSdpDelimiterSpace) {
452 *pos = line_begin;
453 return false;
454 }
455 return true;
456}
457
458// Init |os| to "|type|=|value|".
459static void InitLine(const char type,
460 const std::string& value,
461 std::ostringstream* os) {
462 os->str("");
463 *os << type << kSdpDelimiterEqual << value;
464}
465
466// Init |os| to "a=|attribute|".
467static void InitAttrLine(const std::string& attribute, std::ostringstream* os) {
468 InitLine(kLineTypeAttributes, attribute, os);
469}
470
471// Writes a SDP attribute line based on |attribute| and |value| to |message|.
472static void AddAttributeLine(const std::string& attribute, int value,
473 std::string* message) {
474 std::ostringstream os;
475 InitAttrLine(attribute, &os);
476 os << kSdpDelimiterColon << value;
477 AddLine(os.str(), message);
478}
479
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000480static bool IsLineType(const std::string& message,
481 const char type,
482 size_t line_start) {
483 if (message.size() < line_start + kLinePrefixLength) {
484 return false;
485 }
486 const char* cmessage = message.c_str();
487 return (cmessage[line_start] == type &&
488 cmessage[line_start + 1] == kSdpDelimiterEqual);
489}
490
491static bool IsLineType(const std::string& line,
492 const char type) {
493 return IsLineType(line, type, 0);
494}
495
496static bool GetLineWithType(const std::string& message, size_t* pos,
497 std::string* line, const char type) {
498 if (!IsLineType(message, type, *pos)) {
499 return false;
500 }
501
502 if (!GetLine(message, pos, line))
503 return false;
504
505 return true;
506}
507
508static bool HasAttribute(const std::string& line,
509 const std::string& attribute) {
510 return (line.compare(kLinePrefixLength, attribute.size(), attribute) == 0);
511}
512
Peter Boström0c4e06b2015-10-07 12:23:21 +0200513static bool AddSsrcLine(uint32_t ssrc_id,
514 const std::string& attribute,
515 const std::string& value,
516 std::string* message) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000517 // RFC 5576
518 // a=ssrc:<ssrc-id> <attribute>:<value>
519 std::ostringstream os;
520 InitAttrLine(kAttributeSsrc, &os);
521 os << kSdpDelimiterColon << ssrc_id << kSdpDelimiterSpace
522 << attribute << kSdpDelimiterColon << value;
523 return AddLine(os.str(), message);
524}
525
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000526// Get value only from <attribute>:<value>.
527static bool GetValue(const std::string& message, const std::string& attribute,
528 std::string* value, SdpParseError* error) {
529 std::string leftpart;
Donald Curtis0e07f922015-05-15 09:21:23 -0700530 if (!rtc::tokenize_first(message, kSdpDelimiterColon, &leftpart, value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000531 return ParseFailedGetValue(message, attribute, error);
532 }
533 // The left part should end with the expected attribute.
534 if (leftpart.length() < attribute.length() ||
535 leftpart.compare(leftpart.length() - attribute.length(),
536 attribute.length(), attribute) != 0) {
537 return ParseFailedGetValue(message, attribute, error);
538 }
539 return true;
540}
541
542static bool CaseInsensitiveFind(std::string str1, std::string str2) {
543 std::transform(str1.begin(), str1.end(), str1.begin(),
544 ::tolower);
545 std::transform(str2.begin(), str2.end(), str2.begin(),
546 ::tolower);
547 return str1.find(str2) != std::string::npos;
548}
549
wu@webrtc.org5e760e72014-04-02 23:19:09 +0000550template <class T>
551static bool GetValueFromString(const std::string& line,
552 const std::string& s,
553 T* t,
554 SdpParseError* error) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000555 if (!rtc::FromString(s, t)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +0000556 std::ostringstream description;
557 description << "Invalid value: " << s << ".";
558 return ParseFailed(line, description.str(), error);
559 }
560 return true;
561}
562
pkasting@chromium.orge9facf82015-02-17 20:36:28 +0000563static bool GetPayloadTypeFromString(const std::string& line,
564 const std::string& s,
565 int* payload_type,
566 SdpParseError* error) {
567 return GetValueFromString(line, s, payload_type, error) &&
568 cricket::IsValidRtpPayloadType(*payload_type);
569}
570
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800571// |msid_stream_id| and |msid_track_id| represent the stream/track ID from the
572// "a=msid" attribute, if it exists. They are empty if the attribute does not
573// exist.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000574void CreateTracksFromSsrcInfos(const SsrcInfoVec& ssrc_infos,
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800575 const std::string& msid_stream_id,
576 const std::string& msid_track_id,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000577 StreamParamsVec* tracks) {
nisseede5da42017-01-12 05:15:36 -0800578 RTC_DCHECK(tracks != NULL);
579 RTC_DCHECK(msid_stream_id.empty() == msid_track_id.empty());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000580 for (SsrcInfoVec::const_iterator ssrc_info = ssrc_infos.begin();
581 ssrc_info != ssrc_infos.end(); ++ssrc_info) {
582 if (ssrc_info->cname.empty()) {
583 continue;
584 }
585
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800586 std::string stream_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000587 std::string track_id;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800588 if (ssrc_info->stream_id.empty() && !ssrc_info->mslabel.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000589 // If there's no msid and there's mslabel, we consider this is a sdp from
590 // a older version of client that doesn't support msid.
591 // In that case, we use the mslabel and label to construct the track.
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800592 stream_id = ssrc_info->mslabel;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000593 track_id = ssrc_info->label;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800594 } else if (ssrc_info->stream_id.empty() && !msid_stream_id.empty()) {
595 // If there's no msid in the SSRC attributes, but there's a global one
596 // (from a=msid), use that. This is the case with unified plan SDP.
597 stream_id = msid_stream_id;
598 track_id = msid_track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000599 } else {
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800600 stream_id = ssrc_info->stream_id;
deadbeef9d3584c2016-02-16 17:54:10 -0800601 track_id = ssrc_info->track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000602 }
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800603 // If a stream/track ID wasn't populated from the SSRC attributes OR the
604 // msid attribute, use default/random values.
605 if (stream_id.empty()) {
606 stream_id = kDefaultMsid;
607 }
608 if (track_id.empty()) {
609 // TODO(ronghuawu): What should we do if the track id doesn't appear?
610 // Create random string (which will be used as track label later)?
611 track_id = rtc::CreateRandomString(8);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000612 }
613
614 StreamParamsVec::iterator track = tracks->begin();
615 for (; track != tracks->end(); ++track) {
616 if (track->id == track_id) {
617 break;
618 }
619 }
620 if (track == tracks->end()) {
621 // If we don't find an existing track, create a new one.
622 tracks->push_back(StreamParams());
623 track = tracks->end() - 1;
624 }
625 track->add_ssrc(ssrc_info->ssrc_id);
626 track->cname = ssrc_info->cname;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800627 track->sync_label = stream_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000628 track->id = track_id;
629 }
630}
631
632void GetMediaStreamLabels(const ContentInfo* content,
633 std::set<std::string>* labels) {
634 const MediaContentDescription* media_desc =
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000635 static_cast<const MediaContentDescription*>(
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000636 content->description);
637 const cricket::StreamParamsVec& streams = media_desc->streams();
638 for (cricket::StreamParamsVec::const_iterator it = streams.begin();
639 it != streams.end(); ++it) {
640 labels->insert(it->sync_label);
641 }
642}
643
644// RFC 5245
645// It is RECOMMENDED that default candidates be chosen based on the
646// likelihood of those candidates to work with the peer that is being
647// contacted. It is RECOMMENDED that relayed > reflexive > host.
648static const int kPreferenceUnknown = 0;
649static const int kPreferenceHost = 1;
650static const int kPreferenceReflexive = 2;
651static const int kPreferenceRelayed = 3;
652
653static int GetCandidatePreferenceFromType(const std::string& type) {
654 int preference = kPreferenceUnknown;
655 if (type == cricket::LOCAL_PORT_TYPE) {
656 preference = kPreferenceHost;
657 } else if (type == cricket::STUN_PORT_TYPE) {
658 preference = kPreferenceReflexive;
659 } else if (type == cricket::RELAY_PORT_TYPE) {
660 preference = kPreferenceRelayed;
661 } else {
nissec80e7412017-01-11 05:56:46 -0800662 RTC_NOTREACHED();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000663 }
664 return preference;
665}
666
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000667// Get ip and port of the default destination from the |candidates| with the
668// given value of |component_id|. The default candidate should be the one most
669// likely to work, typically IPv4 relay.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000670// RFC 5245
671// The value of |component_id| currently supported are 1 (RTP) and 2 (RTCP).
672// TODO: Decide the default destination in webrtcsession and
673// pass it down via SessionDescription.
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000674static void GetDefaultDestination(
675 const std::vector<Candidate>& candidates,
676 int component_id, std::string* port,
677 std::string* ip, std::string* addr_type) {
perkj@webrtc.orgd105cc82014-11-07 11:22:06 +0000678 *addr_type = kConnectionIpv4Addrtype;
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000679 *port = kDummyPort;
680 *ip = kDummyAddress;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000681 int current_preference = kPreferenceUnknown;
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000682 int current_family = AF_UNSPEC;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000683 for (std::vector<Candidate>::const_iterator it = candidates.begin();
684 it != candidates.end(); ++it) {
685 if (it->component() != component_id) {
686 continue;
687 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000688 // Default destination should be UDP only.
689 if (it->protocol() != cricket::UDP_PROTOCOL_NAME) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000690 continue;
691 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000692 const int preference = GetCandidatePreferenceFromType(it->type());
693 const int family = it->address().ipaddr().family();
694 // See if this candidate is more preferable then the current one if it's the
695 // same family. Or if the current family is IPv4 already so we could safely
696 // ignore all IPv6 ones. WebRTC bug 4269.
697 // http://code.google.com/p/webrtc/issues/detail?id=4269
698 if ((preference <= current_preference && current_family == family) ||
699 (current_family == AF_INET && family == AF_INET6)) {
700 continue;
701 }
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000702 if (family == AF_INET) {
703 addr_type->assign(kConnectionIpv4Addrtype);
704 } else if (family == AF_INET6) {
705 addr_type->assign(kConnectionIpv6Addrtype);
706 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000707 current_preference = preference;
708 current_family = family;
709 *port = it->address().PortAsString();
710 *ip = it->address().ipaddr().ToString();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000711 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000712}
713
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000714// Update |mline|'s default destination and append a c line after it.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000715static void UpdateMediaDefaultDestination(
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000716 const std::vector<Candidate>& candidates,
jbauch083b73f2015-07-16 02:46:32 -0700717 const std::string& mline,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000718 std::string* message) {
719 std::string new_lines;
720 AddLine(mline, &new_lines);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000721 // RFC 4566
722 // m=<media> <port> <proto> <fmt> ...
723 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000724 rtc::split(mline, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000725 if (fields.size() < 3) {
726 return;
727 }
728
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000729 std::ostringstream os;
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000730 std::string rtp_port, rtp_ip, addr_type;
731 GetDefaultDestination(candidates, ICE_CANDIDATE_COMPONENT_RTP,
732 &rtp_port, &rtp_ip, &addr_type);
733 // Found default RTP candidate.
734 // RFC 5245
735 // The default candidates are added to the SDP as the default
736 // destination for media. For streams based on RTP, this is done by
737 // placing the IP address and port of the RTP candidate into the c and m
738 // lines, respectively.
739 // Update the port in the m line.
740 // If this is a m-line with port equal to 0, we don't change it.
741 if (fields[1] != kMediaPortRejected) {
742 new_lines.replace(fields[0].size() + 1,
743 fields[1].size(),
744 rtp_port);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000745 }
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000746 // Add the c line.
747 // RFC 4566
748 // c=<nettype> <addrtype> <connection-address>
749 InitLine(kLineTypeConnection, kConnectionNettype, &os);
750 os << " " << addr_type << " " << rtp_ip;
751 AddLine(os.str(), &new_lines);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000752 message->append(new_lines);
753}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000754
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000755// Gets "a=rtcp" line if found default RTCP candidate from |candidates|.
756static std::string GetRtcpLine(const std::vector<Candidate>& candidates) {
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000757 std::string rtcp_line, rtcp_port, rtcp_ip, addr_type;
758 GetDefaultDestination(candidates, ICE_CANDIDATE_COMPONENT_RTCP,
759 &rtcp_port, &rtcp_ip, &addr_type);
760 // Found default RTCP candidate.
761 // RFC 5245
762 // If the agent is utilizing RTCP, it MUST encode the RTCP candidate
763 // using the a=rtcp attribute as defined in RFC 3605.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000764
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000765 // RFC 3605
766 // rtcp-attribute = "a=rtcp:" port [nettype space addrtype space
767 // connection-address] CRLF
768 std::ostringstream os;
769 InitAttrLine(kAttributeRtcp, &os);
770 os << kSdpDelimiterColon
771 << rtcp_port << " "
772 << kConnectionNettype << " "
773 << addr_type << " "
774 << rtcp_ip;
775 rtcp_line = os.str();
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000776 return rtcp_line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000777}
778
779// Get candidates according to the mline index from SessionDescriptionInterface.
780static void GetCandidatesByMindex(const SessionDescriptionInterface& desci,
781 int mline_index,
782 std::vector<Candidate>* candidates) {
783 if (!candidates) {
784 return;
785 }
786 const IceCandidateCollection* cc = desci.candidates(mline_index);
787 for (size_t i = 0; i < cc->count(); ++i) {
788 const IceCandidateInterface* candidate = cc->at(i);
789 candidates->push_back(candidate->candidate());
790 }
791}
792
deadbeef9d3584c2016-02-16 17:54:10 -0800793std::string SdpSerialize(const JsepSessionDescription& jdesc,
794 bool unified_plan_sdp) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000795 const cricket::SessionDescription* desc = jdesc.description();
796 if (!desc) {
797 return "";
798 }
799
800 std::string message;
801
802 // Session Description.
803 AddLine(kSessionVersion, &message);
804 // Session Origin
805 // RFC 4566
806 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
807 // <unicast-address>
808 std::ostringstream os;
809 InitLine(kLineTypeOrigin, kSessionOriginUsername, &os);
jbauch083b73f2015-07-16 02:46:32 -0700810 const std::string& session_id = jdesc.session_id().empty() ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000811 kSessionOriginSessionId : jdesc.session_id();
jbauch083b73f2015-07-16 02:46:32 -0700812 const std::string& session_version = jdesc.session_version().empty() ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000813 kSessionOriginSessionVersion : jdesc.session_version();
814 os << " " << session_id << " " << session_version << " "
815 << kSessionOriginNettype << " " << kSessionOriginAddrtype << " "
816 << kSessionOriginAddress;
817 AddLine(os.str(), &message);
818 AddLine(kSessionName, &message);
819
820 // Time Description.
821 AddLine(kTimeDescription, &message);
822
823 // Group
824 if (desc->HasGroup(cricket::GROUP_TYPE_BUNDLE)) {
825 std::string group_line = kAttrGroup;
826 const cricket::ContentGroup* group =
827 desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
nisseede5da42017-01-12 05:15:36 -0800828 RTC_DCHECK(group != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000829 const cricket::ContentNames& content_names = group->content_names();
830 for (cricket::ContentNames::const_iterator it = content_names.begin();
831 it != content_names.end(); ++it) {
832 group_line.append(" ");
833 group_line.append(*it);
834 }
835 AddLine(group_line, &message);
836 }
837
838 // MediaStream semantics
839 InitAttrLine(kAttributeMsidSemantics, &os);
840 os << kSdpDelimiterColon << " " << kMediaStreamSemantic;
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000841
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000842 std::set<std::string> media_stream_labels;
843 const ContentInfo* audio_content = GetFirstAudioContent(desc);
844 if (audio_content)
845 GetMediaStreamLabels(audio_content, &media_stream_labels);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000846
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000847 const ContentInfo* video_content = GetFirstVideoContent(desc);
848 if (video_content)
849 GetMediaStreamLabels(video_content, &media_stream_labels);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000850
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000851 for (std::set<std::string>::const_iterator it =
852 media_stream_labels.begin(); it != media_stream_labels.end(); ++it) {
853 os << " " << *it;
854 }
855 AddLine(os.str(), &message);
856
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000857 // Preserve the order of the media contents.
858 int mline_index = -1;
859 for (cricket::ContentInfos::const_iterator it = desc->contents().begin();
860 it != desc->contents().end(); ++it) {
861 const MediaContentDescription* mdesc =
862 static_cast<const MediaContentDescription*>(it->description);
863 std::vector<Candidate> candidates;
864 GetCandidatesByMindex(jdesc, ++mline_index, &candidates);
deadbeef9d3584c2016-02-16 17:54:10 -0800865 BuildMediaDescription(&*it, desc->GetTransportInfoByName(it->name),
866 mdesc->type(), candidates, unified_plan_sdp,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000867 &message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000868 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000869 return message;
870}
871
872// Serializes the passed in IceCandidateInterface to a SDP string.
873// candidate - The candidate to be serialized.
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700874std::string SdpSerializeCandidate(const IceCandidateInterface& candidate) {
875 return SdpSerializeCandidate(candidate.candidate());
876}
877
878// Serializes a cricket Candidate.
879std::string SdpSerializeCandidate(const cricket::Candidate& candidate) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000880 std::string message;
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700881 std::vector<cricket::Candidate> candidates(1, candidate);
honghaiza54a0802015-12-16 18:37:23 -0800882 BuildCandidate(candidates, true, &message);
wu@webrtc.orgec9f5fb2014-06-24 17:05:10 +0000883 // From WebRTC draft section 4.8.1.1 candidate-attribute will be
884 // just candidate:<candidate> not a=candidate:<blah>CRLF
nisseede5da42017-01-12 05:15:36 -0800885 RTC_DCHECK(message.find("a=") == 0);
wu@webrtc.orgec9f5fb2014-06-24 17:05:10 +0000886 message.erase(0, 2);
nisseede5da42017-01-12 05:15:36 -0800887 RTC_DCHECK(message.find(kLineBreak) == message.size() - 2);
wu@webrtc.orgec9f5fb2014-06-24 17:05:10 +0000888 message.resize(message.size() - 2);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000889 return message;
890}
891
892bool SdpDeserialize(const std::string& message,
893 JsepSessionDescription* jdesc,
894 SdpParseError* error) {
895 std::string session_id;
896 std::string session_version;
Peter Thatcher7cbd1882015-09-17 18:54:52 -0700897 TransportDescription session_td("", "");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000898 RtpHeaderExtensions session_extmaps;
899 cricket::SessionDescription* desc = new cricket::SessionDescription();
900 std::vector<JsepIceCandidate*> candidates;
901 size_t current_pos = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000902
903 // Session Description
904 if (!ParseSessionDescription(message, &current_pos, &session_id,
deadbeefc80741f2015-10-22 13:14:45 -0700905 &session_version, &session_td, &session_extmaps,
906 desc, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000907 delete desc;
908 return false;
909 }
910
911 // Media Description
deadbeefc80741f2015-10-22 13:14:45 -0700912 if (!ParseMediaDescription(message, session_td, session_extmaps, &current_pos,
913 desc, &candidates, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000914 delete desc;
915 for (std::vector<JsepIceCandidate*>::const_iterator
916 it = candidates.begin(); it != candidates.end(); ++it) {
917 delete *it;
918 }
919 return false;
920 }
921
922 jdesc->Initialize(desc, session_id, session_version);
923
924 for (std::vector<JsepIceCandidate*>::const_iterator
925 it = candidates.begin(); it != candidates.end(); ++it) {
926 jdesc->AddCandidate(*it);
927 delete *it;
928 }
929 return true;
930}
931
932bool SdpDeserializeCandidate(const std::string& message,
933 JsepIceCandidate* jcandidate,
934 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -0800935 RTC_DCHECK(jcandidate != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000936 Candidate candidate;
937 if (!ParseCandidate(message, &candidate, error, true)) {
938 return false;
939 }
940 jcandidate->SetCandidate(candidate);
941 return true;
942}
943
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700944bool SdpDeserializeCandidate(const std::string& transport_name,
945 const std::string& message,
946 cricket::Candidate* candidate,
947 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -0800948 RTC_DCHECK(candidate != nullptr);
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700949 if (!ParseCandidate(message, candidate, error, true)) {
950 return false;
951 }
952 candidate->set_transport_name(transport_name);
953 return true;
954}
955
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000956bool ParseCandidate(const std::string& message, Candidate* candidate,
957 SdpParseError* error, bool is_raw) {
nisseede5da42017-01-12 05:15:36 -0800958 RTC_DCHECK(candidate != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000959
960 // Get the first line from |message|.
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000961 std::string first_line = message;
962 size_t pos = 0;
963 GetLine(message, &pos, &first_line);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000964
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000965 // Makes sure |message| contains only one line.
966 if (message.size() > first_line.size()) {
967 std::string left, right;
Donald Curtis0e07f922015-05-15 09:21:23 -0700968 if (rtc::tokenize_first(message, kNewLine, &left, &right) &&
969 !right.empty()) {
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000970 return ParseFailed(message, 0, "Expect one line only", error);
971 }
972 }
973
974 // From WebRTC draft section 4.8.1.1 candidate-attribute should be
975 // candidate:<candidate> when trickled, but we still support
976 // a=candidate:<blah>CRLF for backward compatibility and for parsing a line
977 // from the SDP.
978 if (IsLineType(first_line, kLineTypeAttributes)) {
979 first_line = first_line.substr(kLinePrefixLength);
980 }
981
982 std::string attribute_candidate;
983 std::string candidate_value;
984
985 // |first_line| must be in the form of "candidate:<value>".
Donald Curtis144d0182015-05-15 13:14:24 -0700986 if (!rtc::tokenize_first(first_line, kSdpDelimiterColon, &attribute_candidate,
987 &candidate_value) ||
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000988 attribute_candidate != kAttributeCandidate) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000989 if (is_raw) {
990 std::ostringstream description;
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000991 description << "Expect line: " << kAttributeCandidate
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000992 << ":" << "<candidate-str>";
993 return ParseFailed(first_line, 0, description.str(), error);
994 } else {
995 return ParseFailedExpectLine(first_line, 0, kLineTypeAttributes,
996 kAttributeCandidate, error);
997 }
998 }
999
1000 std::vector<std::string> fields;
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001001 rtc::split(candidate_value, kSdpDelimiterSpace, &fields);
1002
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001003 // RFC 5245
1004 // a=candidate:<foundation> <component-id> <transport> <priority>
1005 // <connection-address> <port> typ <candidate-types>
1006 // [raddr <connection-address>] [rport <port>]
1007 // *(SP extension-att-name SP extension-att-value)
1008 const size_t expected_min_fields = 8;
1009 if (fields.size() < expected_min_fields ||
1010 (fields[6] != kAttributeCandidateTyp)) {
1011 return ParseFailedExpectMinFieldNum(first_line, expected_min_fields, error);
1012 }
jbauch083b73f2015-07-16 02:46:32 -07001013 const std::string& foundation = fields[0];
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001014
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001015 int component_id = 0;
1016 if (!GetValueFromString(first_line, fields[1], &component_id, error)) {
1017 return false;
1018 }
jbauch083b73f2015-07-16 02:46:32 -07001019 const std::string& transport = fields[2];
Peter Boström0c4e06b2015-10-07 12:23:21 +02001020 uint32_t priority = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001021 if (!GetValueFromString(first_line, fields[3], &priority, error)) {
1022 return false;
1023 }
jbauch083b73f2015-07-16 02:46:32 -07001024 const std::string& connection_address = fields[4];
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001025 int port = 0;
1026 if (!GetValueFromString(first_line, fields[5], &port, error)) {
1027 return false;
1028 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001029 SocketAddress address(connection_address, port);
1030
1031 cricket::ProtocolType protocol;
hnslb68cc752016-12-13 10:33:41 -08001032 if (!StringToProto(transport.c_str(), &protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001033 return ParseFailed(first_line, "Unsupported transport type.", error);
1034 }
hnslb68cc752016-12-13 10:33:41 -08001035 switch (protocol) {
1036 case cricket::PROTO_UDP:
1037 case cricket::PROTO_TCP:
1038 case cricket::PROTO_SSLTCP:
1039 // Supported protocol.
1040 break;
1041 default:
1042 return ParseFailed(first_line, "Unsupported transport type.", error);
1043 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001044
1045 std::string candidate_type;
jbauch083b73f2015-07-16 02:46:32 -07001046 const std::string& type = fields[7];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001047 if (type == kCandidateHost) {
1048 candidate_type = cricket::LOCAL_PORT_TYPE;
1049 } else if (type == kCandidateSrflx) {
1050 candidate_type = cricket::STUN_PORT_TYPE;
1051 } else if (type == kCandidateRelay) {
1052 candidate_type = cricket::RELAY_PORT_TYPE;
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001053 } else if (type == kCandidatePrflx) {
1054 candidate_type = cricket::PRFLX_PORT_TYPE;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001055 } else {
1056 return ParseFailed(first_line, "Unsupported candidate type.", error);
1057 }
1058
1059 size_t current_position = expected_min_fields;
1060 SocketAddress related_address;
1061 // The 2 optional fields for related address
1062 // [raddr <connection-address>] [rport <port>]
1063 if (fields.size() >= (current_position + 2) &&
1064 fields[current_position] == kAttributeCandidateRaddr) {
1065 related_address.SetIP(fields[++current_position]);
1066 ++current_position;
1067 }
1068 if (fields.size() >= (current_position + 2) &&
1069 fields[current_position] == kAttributeCandidateRport) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001070 int port = 0;
1071 if (!GetValueFromString(
1072 first_line, fields[++current_position], &port, error)) {
1073 return false;
1074 }
1075 related_address.SetPort(port);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001076 ++current_position;
1077 }
1078
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001079 // If this is a TCP candidate, it has additional extension as defined in
1080 // RFC 6544.
1081 std::string tcptype;
1082 if (fields.size() >= (current_position + 2) &&
1083 fields[current_position] == kTcpCandidateType) {
1084 tcptype = fields[++current_position];
1085 ++current_position;
1086
1087 if (tcptype != cricket::TCPTYPE_ACTIVE_STR &&
1088 tcptype != cricket::TCPTYPE_PASSIVE_STR &&
1089 tcptype != cricket::TCPTYPE_SIMOPEN_STR) {
1090 return ParseFailed(first_line, "Invalid TCP candidate type.", error);
1091 }
1092
1093 if (protocol != cricket::PROTO_TCP) {
1094 return ParseFailed(first_line, "Invalid non-TCP candidate", error);
1095 }
1096 }
1097
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001098 // Extension
honghaiza54a0802015-12-16 18:37:23 -08001099 // Though non-standard, we support the ICE ufrag and pwd being signaled on
1100 // the candidate to avoid issues with confusing which generation a candidate
1101 // belongs to when trickling multiple generations at the same time.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001102 std::string username;
1103 std::string password;
Peter Boström0c4e06b2015-10-07 12:23:21 +02001104 uint32_t generation = 0;
honghaiza0c44ea2016-03-23 16:07:48 -07001105 uint16_t network_id = 0;
1106 uint16_t network_cost = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001107 for (size_t i = current_position; i + 1 < fields.size(); ++i) {
1108 // RFC 5245
1109 // *(SP extension-att-name SP extension-att-value)
1110 if (fields[i] == kAttributeCandidateGeneration) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001111 if (!GetValueFromString(first_line, fields[++i], &generation, error)) {
1112 return false;
1113 }
honghaiza54a0802015-12-16 18:37:23 -08001114 } else if (fields[i] == kAttributeCandidateUfrag) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001115 username = fields[++i];
honghaiza54a0802015-12-16 18:37:23 -08001116 } else if (fields[i] == kAttributeCandidatePwd) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001117 password = fields[++i];
honghaiza0c44ea2016-03-23 16:07:48 -07001118 } else if (fields[i] == kAttributeCandidateNetworkId) {
1119 if (!GetValueFromString(first_line, fields[++i], &network_id, error)) {
1120 return false;
1121 }
honghaize1a0c942016-02-16 14:54:56 -08001122 } else if (fields[i] == kAttributeCandidateNetworkCost) {
1123 if (!GetValueFromString(first_line, fields[++i], &network_cost, error)) {
1124 return false;
1125 }
Honghai Zhang351d77b2016-05-20 15:08:29 -07001126 network_cost = std::min(network_cost, rtc::kNetworkCostMax);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001127 } else {
1128 // Skip the unknown extension.
1129 ++i;
1130 }
1131 }
1132
guoweis@webrtc.org61c12472015-01-15 06:53:07 +00001133 *candidate = Candidate(component_id, cricket::ProtoToString(protocol),
guoweis@webrtc.org950c5182014-12-16 23:01:31 +00001134 address, priority, username, password, candidate_type,
honghaiza0c44ea2016-03-23 16:07:48 -07001135 generation, foundation, network_id, network_cost);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001136 candidate->set_related_address(related_address);
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001137 candidate->set_tcptype(tcptype);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001138 return true;
1139}
1140
1141bool ParseIceOptions(const std::string& line,
1142 std::vector<std::string>* transport_options,
1143 SdpParseError* error) {
1144 std::string ice_options;
1145 if (!GetValue(line, kAttributeIceOption, &ice_options, error)) {
1146 return false;
1147 }
1148 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001149 rtc::split(ice_options, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001150 for (size_t i = 0; i < fields.size(); ++i) {
1151 transport_options->push_back(fields[i]);
1152 }
1153 return true;
1154}
1155
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001156bool ParseSctpPort(const std::string& line,
1157 int* sctp_port,
1158 SdpParseError* error) {
1159 // draft-ietf-mmusic-sctp-sdp-07
1160 // a=sctp-port
1161 std::vector<std::string> fields;
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001162 const size_t expected_min_fields = 2;
lally69f57602015-10-08 10:15:04 -07001163 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColon, &fields);
1164 if (fields.size() < expected_min_fields) {
1165 fields.resize(0);
1166 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpace, &fields);
1167 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001168 if (fields.size() < expected_min_fields) {
1169 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1170 }
1171 if (!rtc::FromString(fields[1], sctp_port)) {
lally69f57602015-10-08 10:15:04 -07001172 return ParseFailed(line, "Invalid sctp port value.", error);
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001173 }
1174 return true;
1175}
1176
isheriff6f8d6862016-05-26 11:24:55 -07001177bool ParseExtmap(const std::string& line,
1178 RtpExtension* extmap,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001179 SdpParseError* error) {
1180 // RFC 5285
1181 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1182 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001183 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001184 kSdpDelimiterSpace, &fields);
1185 const size_t expected_min_fields = 2;
1186 if (fields.size() < expected_min_fields) {
1187 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1188 }
1189 std::string uri = fields[1];
1190
1191 std::string value_direction;
1192 if (!GetValue(fields[0], kAttributeExtmap, &value_direction, error)) {
1193 return false;
1194 }
1195 std::vector<std::string> sub_fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001196 rtc::split(value_direction, kSdpDelimiterSlash, &sub_fields);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001197 int value = 0;
1198 if (!GetValueFromString(line, sub_fields[0], &value, error)) {
1199 return false;
1200 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001201
isheriff6f8d6862016-05-26 11:24:55 -07001202 *extmap = RtpExtension(uri, value);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001203 return true;
1204}
1205
1206void BuildMediaDescription(const ContentInfo* content_info,
1207 const TransportInfo* transport_info,
1208 const MediaType media_type,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001209 const std::vector<Candidate>& candidates,
deadbeef9d3584c2016-02-16 17:54:10 -08001210 bool unified_plan_sdp,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001211 std::string* message) {
nisseede5da42017-01-12 05:15:36 -08001212 RTC_DCHECK(message != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001213 if (content_info == NULL || message == NULL) {
1214 return;
1215 }
1216 // TODO: Rethink if we should use sprintfn instead of stringstream.
1217 // According to the style guide, streams should only be used for logging.
1218 // http://google-styleguide.googlecode.com/svn/
1219 // trunk/cppguide.xml?showone=Streams#Streams
1220 std::ostringstream os;
1221 const MediaContentDescription* media_desc =
henrike@webrtc.org28654cb2013-07-22 21:07:49 +00001222 static_cast<const MediaContentDescription*>(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001223 content_info->description);
nisseede5da42017-01-12 05:15:36 -08001224 RTC_DCHECK(media_desc != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001225
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001226 int sctp_port = cricket::kSctpDefaultPort;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001227
1228 // RFC 4566
1229 // m=<media> <port> <proto> <fmt>
1230 // fmt is a list of payload type numbers that MAY be used in the session.
1231 const char* type = NULL;
1232 if (media_type == cricket::MEDIA_TYPE_AUDIO)
1233 type = kMediaTypeAudio;
1234 else if (media_type == cricket::MEDIA_TYPE_VIDEO)
1235 type = kMediaTypeVideo;
1236 else if (media_type == cricket::MEDIA_TYPE_DATA)
1237 type = kMediaTypeData;
1238 else
nissec80e7412017-01-11 05:56:46 -08001239 RTC_NOTREACHED();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001240
1241 std::string fmt;
1242 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
1243 const VideoContentDescription* video_desc =
1244 static_cast<const VideoContentDescription*>(media_desc);
1245 for (std::vector<cricket::VideoCodec>::const_iterator it =
1246 video_desc->codecs().begin();
1247 it != video_desc->codecs().end(); ++it) {
1248 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001249 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001250 }
1251 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
1252 const AudioContentDescription* audio_desc =
1253 static_cast<const AudioContentDescription*>(media_desc);
1254 for (std::vector<cricket::AudioCodec>::const_iterator it =
1255 audio_desc->codecs().begin();
1256 it != audio_desc->codecs().end(); ++it) {
1257 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001258 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001259 }
1260 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001261 const DataContentDescription* data_desc =
1262 static_cast<const DataContentDescription*>(media_desc);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001263 if (IsDtlsSctp(media_desc->protocol())) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001264 fmt.append(" ");
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001265
1266 for (std::vector<cricket::DataCodec>::const_iterator it =
1267 data_desc->codecs().begin();
1268 it != data_desc->codecs().end(); ++it) {
solenberg9fa49752016-10-08 13:02:44 -07001269 if (cricket::CodecNamesEq(it->name, cricket::kGoogleSctpDataCodecName)
1270 && it->GetParam(cricket::kCodecParamPort, &sctp_port)) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001271 break;
1272 }
1273 }
1274
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001275 fmt.append(rtc::ToString<int>(sctp_port));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001276 } else {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001277 for (std::vector<cricket::DataCodec>::const_iterator it =
1278 data_desc->codecs().begin();
1279 it != data_desc->codecs().end(); ++it) {
1280 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001281 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001282 }
1283 }
1284 }
1285 // The fmt must never be empty. If no codecs are found, set the fmt attribute
1286 // to 0.
1287 if (fmt.empty()) {
1288 fmt = " 0";
1289 }
1290
deadbeef25ed4352016-12-12 18:37:36 -08001291 // The port number in the m line will be updated later when associated with
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001292 // the candidates.
deadbeef25ed4352016-12-12 18:37:36 -08001293 //
1294 // A port value of 0 indicates that the m= section is rejected.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001295 // RFC 3264
1296 // To reject an offered stream, the port number in the corresponding stream in
1297 // the answer MUST be set to zero.
deadbeef25ed4352016-12-12 18:37:36 -08001298 //
1299 // However, the BUNDLE draft adds a new meaning to port zero, when used along
1300 // with a=bundle-only.
1301 const std::string& port =
1302 (content_info->rejected || content_info->bundle_only) ? kMediaPortRejected
1303 : kDummyPort;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001304
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001305 rtc::SSLFingerprint* fp = (transport_info) ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001306 transport_info->description.identity_fingerprint.get() : NULL;
1307
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001308 // Add the m and c lines.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001309 InitLine(kLineTypeMedia, type, &os);
1310 os << " " << port << " " << media_desc->protocol() << fmt;
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001311 std::string mline = os.str();
1312 UpdateMediaDefaultDestination(candidates, mline, message);
1313
1314 // RFC 4566
1315 // b=AS:<bandwidth>
Peter Thatcherc0c3a862015-06-24 15:31:25 -07001316 if (media_desc->bandwidth() >= 1000) {
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001317 InitLine(kLineTypeSessionBandwidth, kApplicationSpecificMaximum, &os);
1318 os << kSdpDelimiterColon << (media_desc->bandwidth() / 1000);
1319 AddLine(os.str(), message);
1320 }
1321
deadbeef25ed4352016-12-12 18:37:36 -08001322 // Add the a=bundle-only line.
1323 if (content_info->bundle_only) {
1324 InitAttrLine(kAttributeBundleOnly, &os);
1325 AddLine(os.str(), message);
1326 }
1327
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001328 // Add the a=rtcp line.
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001329 if (IsRtp(media_desc->protocol())) {
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001330 std::string rtcp_line = GetRtcpLine(candidates);
1331 if (!rtcp_line.empty()) {
1332 AddLine(rtcp_line, message);
1333 }
1334 }
1335
honghaiza54a0802015-12-16 18:37:23 -08001336 // Build the a=candidate lines. We don't include ufrag and pwd in the
1337 // candidates in the SDP to avoid redundancy.
1338 BuildCandidate(candidates, false, message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001339
1340 // Use the transport_info to build the media level ice-ufrag and ice-pwd.
1341 if (transport_info) {
1342 // RFC 5245
1343 // ice-pwd-att = "ice-pwd" ":" password
1344 // ice-ufrag-att = "ice-ufrag" ":" ufrag
1345 // ice-ufrag
deadbeef3f7219b2015-12-28 15:17:14 -08001346 if (!transport_info->description.ice_ufrag.empty()) {
1347 InitAttrLine(kAttributeIceUfrag, &os);
1348 os << kSdpDelimiterColon << transport_info->description.ice_ufrag;
1349 AddLine(os.str(), message);
1350 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001351 // ice-pwd
deadbeef3f7219b2015-12-28 15:17:14 -08001352 if (!transport_info->description.ice_pwd.empty()) {
1353 InitAttrLine(kAttributeIcePwd, &os);
1354 os << kSdpDelimiterColon << transport_info->description.ice_pwd;
1355 AddLine(os.str(), message);
1356 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001357
1358 // draft-petithuguenin-mmusic-ice-attributes-level-03
1359 BuildIceOptions(transport_info->description.transport_options, message);
1360
1361 // RFC 4572
1362 // fingerprint-attribute =
1363 // "fingerprint" ":" hash-func SP fingerprint
1364 if (fp) {
1365 // Insert the fingerprint attribute.
1366 InitAttrLine(kAttributeFingerprint, &os);
1367 os << kSdpDelimiterColon
1368 << fp->algorithm << kSdpDelimiterSpace
1369 << fp->GetRfc4572Fingerprint();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001370 AddLine(os.str(), message);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001371
1372 // Inserting setup attribute.
1373 if (transport_info->description.connection_role !=
1374 cricket::CONNECTIONROLE_NONE) {
1375 // Making sure we are not using "passive" mode.
1376 cricket::ConnectionRole role =
1377 transport_info->description.connection_role;
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001378 std::string dtls_role_str;
1379 VERIFY(cricket::ConnectionRoleToString(role, &dtls_role_str));
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001380 InitAttrLine(kAttributeSetup, &os);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001381 os << kSdpDelimiterColon << dtls_role_str;
1382 AddLine(os.str(), message);
1383 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001384 }
1385 }
1386
1387 // RFC 3388
1388 // mid-attribute = "a=mid:" identification-tag
1389 // identification-tag = token
1390 // Use the content name as the mid identification-tag.
1391 InitAttrLine(kAttributeMid, &os);
1392 os << kSdpDelimiterColon << content_info->name;
1393 AddLine(os.str(), message);
1394
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001395 if (IsDtlsSctp(media_desc->protocol())) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001396 BuildSctpContentAttributes(message, sctp_port);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001397 } else if (IsRtp(media_desc->protocol())) {
deadbeef9d3584c2016-02-16 17:54:10 -08001398 BuildRtpContentAttributes(media_desc, media_type, unified_plan_sdp,
1399 message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001400 }
1401}
1402
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001403void BuildSctpContentAttributes(std::string* message, int sctp_port) {
wu@webrtc.org78187522013-10-07 23:32:02 +00001404 // draft-ietf-mmusic-sctp-sdp-04
1405 // a=sctpmap:sctpmap-number protocol [streams]
lally69f57602015-10-08 10:15:04 -07001406 // TODO(lally): switch this over to mmusic-sctp-sdp-12 (or later), with
1407 // 'a=sctp-port:'
wu@webrtc.org78187522013-10-07 23:32:02 +00001408 std::ostringstream os;
1409 InitAttrLine(kAttributeSctpmap, &os);
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001410 os << kSdpDelimiterColon << sctp_port << kSdpDelimiterSpace
wu@webrtc.org78187522013-10-07 23:32:02 +00001411 << kDefaultSctpmapProtocol << kSdpDelimiterSpace
Taylor Brandstetter1d7a6372016-08-24 13:15:27 -07001412 << cricket::kMaxSctpStreams;
wu@webrtc.org78187522013-10-07 23:32:02 +00001413 AddLine(os.str(), message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001414}
1415
deadbeef9d3584c2016-02-16 17:54:10 -08001416// If unified_plan_sdp is true, will use "a=msid".
1417void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
1418 const MediaType media_type,
1419 bool unified_plan_sdp,
1420 std::string* message) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001421 std::ostringstream os;
1422 // RFC 5285
1423 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1424 // The definitions MUST be either all session level or all media level. This
1425 // implementation uses all media level.
1426 for (size_t i = 0; i < media_desc->rtp_header_extensions().size(); ++i) {
1427 InitAttrLine(kAttributeExtmap, &os);
1428 os << kSdpDelimiterColon << media_desc->rtp_header_extensions()[i].id
1429 << kSdpDelimiterSpace << media_desc->rtp_header_extensions()[i].uri;
1430 AddLine(os.str(), message);
1431 }
1432
1433 // RFC 3264
1434 // a=sendrecv || a=sendonly || a=sendrecv || a=inactive
deadbeefc80741f2015-10-22 13:14:45 -07001435 switch (media_desc->direction()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001436 case cricket::MD_INACTIVE:
1437 InitAttrLine(kAttributeInactive, &os);
1438 break;
1439 case cricket::MD_SENDONLY:
1440 InitAttrLine(kAttributeSendOnly, &os);
1441 break;
1442 case cricket::MD_RECVONLY:
1443 InitAttrLine(kAttributeRecvOnly, &os);
1444 break;
1445 case cricket::MD_SENDRECV:
1446 default:
1447 InitAttrLine(kAttributeSendRecv, &os);
1448 break;
1449 }
1450 AddLine(os.str(), message);
1451
deadbeef9d3584c2016-02-16 17:54:10 -08001452 // draft-ietf-mmusic-msid-11
1453 // a=msid:<stream id> <track id>
1454 if (unified_plan_sdp && !media_desc->streams().empty()) {
1455 if (media_desc->streams().size() > 1u) {
1456 LOG(LS_WARNING) << "Trying to serialize unified plan SDP with more than "
1457 << "one track in a media section. Omitting 'a=msid'.";
1458 } else {
1459 auto track = media_desc->streams().begin();
1460 const std::string& stream_id = track->sync_label;
deadbeef9d3584c2016-02-16 17:54:10 -08001461 InitAttrLine(kAttributeMsid, &os);
1462 os << kSdpDelimiterColon << stream_id << kSdpDelimiterSpace << track->id;
1463 AddLine(os.str(), message);
1464 }
1465 }
1466
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001467 // RFC 5761
1468 // a=rtcp-mux
1469 if (media_desc->rtcp_mux()) {
1470 InitAttrLine(kAttributeRtcpMux, &os);
1471 AddLine(os.str(), message);
1472 }
1473
deadbeef13871492015-12-09 12:37:51 -08001474 // RFC 5506
1475 // a=rtcp-rsize
1476 if (media_desc->rtcp_reduced_size()) {
1477 InitAttrLine(kAttributeRtcpReducedSize, &os);
1478 AddLine(os.str(), message);
1479 }
1480
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001481 // RFC 4568
1482 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
1483 for (std::vector<CryptoParams>::const_iterator it =
1484 media_desc->cryptos().begin();
1485 it != media_desc->cryptos().end(); ++it) {
1486 InitAttrLine(kAttributeCrypto, &os);
1487 os << kSdpDelimiterColon << it->tag << " " << it->cipher_suite << " "
1488 << it->key_params;
1489 if (!it->session_params.empty()) {
1490 os << " " << it->session_params;
1491 }
1492 AddLine(os.str(), message);
1493 }
1494
1495 // RFC 4566
1496 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1497 // [/<encodingparameters>]
1498 BuildRtpMap(media_desc, media_type, message);
1499
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001500 for (StreamParamsVec::const_iterator track = media_desc->streams().begin();
1501 track != media_desc->streams().end(); ++track) {
1502 // Require that the track belongs to a media stream,
1503 // ie the sync_label is set. This extra check is necessary since the
1504 // MediaContentDescription always contains a streamparam with an ssrc even
1505 // if no track or media stream have been created.
1506 if (track->sync_label.empty()) continue;
1507
1508 // Build the ssrc-group lines.
1509 for (size_t i = 0; i < track->ssrc_groups.size(); ++i) {
1510 // RFC 5576
1511 // a=ssrc-group:<semantics> <ssrc-id> ...
1512 if (track->ssrc_groups[i].ssrcs.empty()) {
1513 continue;
1514 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001515 InitAttrLine(kAttributeSsrcGroup, &os);
1516 os << kSdpDelimiterColon << track->ssrc_groups[i].semantics;
Peter Boström0c4e06b2015-10-07 12:23:21 +02001517 std::vector<uint32_t>::const_iterator ssrc =
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001518 track->ssrc_groups[i].ssrcs.begin();
1519 for (; ssrc != track->ssrc_groups[i].ssrcs.end(); ++ssrc) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02001520 os << kSdpDelimiterSpace << rtc::ToString<uint32_t>(*ssrc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001521 }
1522 AddLine(os.str(), message);
1523 }
1524 // Build the ssrc lines for each ssrc.
1525 for (size_t i = 0; i < track->ssrcs.size(); ++i) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02001526 uint32_t ssrc = track->ssrcs[i];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001527 // RFC 5576
1528 // a=ssrc:<ssrc-id> cname:<value>
1529 AddSsrcLine(ssrc, kSsrcAttributeCname,
1530 track->cname, message);
1531
1532 // draft-alvestrand-mmusic-msid-00
1533 // a=ssrc:<ssrc-id> msid:identifier [appdata]
deadbeef9d3584c2016-02-16 17:54:10 -08001534 // The appdata consists of the "id" attribute of a MediaStreamTrack,
1535 // which corresponds to the "id" attribute of StreamParams.
1536 const std::string& stream_id = track->sync_label;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001537 InitAttrLine(kAttributeSsrc, &os);
1538 os << kSdpDelimiterColon << ssrc << kSdpDelimiterSpace
deadbeef9d3584c2016-02-16 17:54:10 -08001539 << kSsrcAttributeMsid << kSdpDelimiterColon << stream_id
1540 << kSdpDelimiterSpace << track->id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001541 AddLine(os.str(), message);
1542
deadbeef9d3584c2016-02-16 17:54:10 -08001543 // TODO(ronghuawu): Remove below code which is for backward
1544 // compatibility.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001545 // draft-alvestrand-rtcweb-mid-01
1546 // a=ssrc:<ssrc-id> mslabel:<value>
1547 // The label isn't yet defined.
1548 // a=ssrc:<ssrc-id> label:<value>
1549 AddSsrcLine(ssrc, kSsrcAttributeMslabel, track->sync_label, message);
1550 AddSsrcLine(ssrc, kSSrcAttributeLabel, track->id, message);
1551 }
1552 }
1553}
1554
1555void WriteFmtpHeader(int payload_type, std::ostringstream* os) {
1556 // fmtp header: a=fmtp:|payload_type| <parameters>
1557 // Add a=fmtp
1558 InitAttrLine(kAttributeFmtp, os);
1559 // Add :|payload_type|
1560 *os << kSdpDelimiterColon << payload_type;
1561}
1562
1563void WriteRtcpFbHeader(int payload_type, std::ostringstream* os) {
1564 // rtcp-fb header: a=rtcp-fb:|payload_type|
1565 // <parameters>/<ccm <ccm_parameters>>
1566 // Add a=rtcp-fb
1567 InitAttrLine(kAttributeRtcpFb, os);
1568 // Add :
1569 *os << kSdpDelimiterColon;
1570 if (payload_type == kWildcardPayloadType) {
1571 *os << "*";
1572 } else {
1573 *os << payload_type;
1574 }
1575}
1576
1577void WriteFmtpParameter(const std::string& parameter_name,
1578 const std::string& parameter_value,
1579 std::ostringstream* os) {
1580 // fmtp parameters: |parameter_name|=|parameter_value|
1581 *os << parameter_name << kSdpDelimiterEqual << parameter_value;
1582}
1583
1584void WriteFmtpParameters(const cricket::CodecParameterMap& parameters,
1585 std::ostringstream* os) {
1586 for (cricket::CodecParameterMap::const_iterator fmtp = parameters.begin();
1587 fmtp != parameters.end(); ++fmtp) {
hta62a216e2016-04-15 11:02:14 -07001588 // Parameters are a semicolon-separated list, no spaces.
1589 // The list is separated from the header by a space.
1590 if (fmtp == parameters.begin()) {
1591 *os << kSdpDelimiterSpace;
1592 } else {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001593 *os << kSdpDelimiterSemicolon;
1594 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001595 WriteFmtpParameter(fmtp->first, fmtp->second, os);
1596 }
1597}
1598
1599bool IsFmtpParam(const std::string& name) {
1600 const char* kFmtpParams[] = {
htaa6b99442016-04-12 10:29:17 -07001601 // TODO(hta): Split FMTP parameters apart from parameters in general.
1602 // FMTP parameters are codec specific, not generic.
1603 kCodecParamMinPTime,
1604 kCodecParamSPropStereo,
1605 kCodecParamStereo,
1606 kCodecParamUseInbandFec,
1607 kCodecParamUseDtx,
1608 kCodecParamStartBitrate,
1609 kCodecParamMaxBitrate,
1610 kCodecParamMinBitrate,
1611 kCodecParamMaxQuantization,
1612 kCodecParamSctpProtocol,
1613 kCodecParamSctpStreams,
1614 kCodecParamMaxAverageBitrate,
1615 kCodecParamMaxPlaybackRate,
1616 kCodecParamAssociatedPayloadType,
1617 cricket::kH264FmtpPacketizationMode,
1618 cricket::kH264FmtpLevelAsymmetryAllowed,
brandtr87d7d772016-11-07 03:03:41 -08001619 cricket::kH264FmtpProfileLevelId,
1620 cricket::kFlexfecFmtpRepairWindow};
tfarina5237aaf2015-11-10 23:44:30 -08001621 for (size_t i = 0; i < arraysize(kFmtpParams); ++i) {
htaa6b99442016-04-12 10:29:17 -07001622 if (name.compare(kFmtpParams[i]) == 0) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001623 return true;
1624 }
1625 }
1626 return false;
1627}
1628
1629// Retreives fmtp parameters from |params|, which may contain other parameters
1630// as well, and puts them in |fmtp_parameters|.
1631void GetFmtpParams(const cricket::CodecParameterMap& params,
1632 cricket::CodecParameterMap* fmtp_parameters) {
1633 for (cricket::CodecParameterMap::const_iterator iter = params.begin();
1634 iter != params.end(); ++iter) {
1635 if (IsFmtpParam(iter->first)) {
1636 (*fmtp_parameters)[iter->first] = iter->second;
1637 }
1638 }
1639}
1640
1641template <class T>
1642void AddFmtpLine(const T& codec, std::string* message) {
1643 cricket::CodecParameterMap fmtp_parameters;
1644 GetFmtpParams(codec.params, &fmtp_parameters);
1645 if (fmtp_parameters.empty()) {
1646 // No need to add an fmtp if it will have no (optional) parameters.
1647 return;
1648 }
1649 std::ostringstream os;
1650 WriteFmtpHeader(codec.id, &os);
1651 WriteFmtpParameters(fmtp_parameters, &os);
1652 AddLine(os.str(), message);
1653 return;
1654}
1655
1656template <class T>
1657void AddRtcpFbLines(const T& codec, std::string* message) {
1658 for (std::vector<cricket::FeedbackParam>::const_iterator iter =
1659 codec.feedback_params.params().begin();
1660 iter != codec.feedback_params.params().end(); ++iter) {
1661 std::ostringstream os;
1662 WriteRtcpFbHeader(codec.id, &os);
1663 os << " " << iter->id();
1664 if (!iter->param().empty()) {
1665 os << " " << iter->param();
1666 }
1667 AddLine(os.str(), message);
1668 }
1669}
1670
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001671bool AddSctpDataCodec(DataContentDescription* media_desc,
1672 int sctp_port) {
solenberg9fa49752016-10-08 13:02:44 -07001673 for (const auto& codec : media_desc->codecs()) {
1674 if (cricket::CodecNamesEq(codec.name, cricket::kGoogleSctpDataCodecName)) {
1675 return ParseFailed("",
1676 "Can't have multiple sctp port attributes.",
1677 NULL);
1678 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001679 }
1680 // Add the SCTP Port number as a pseudo-codec "port" parameter
solenberg9fa49752016-10-08 13:02:44 -07001681 cricket::DataCodec codec_port(cricket::kGoogleSctpDataCodecPlType,
deadbeef67cf2c12016-04-13 10:07:16 -07001682 cricket::kGoogleSctpDataCodecName);
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001683 codec_port.SetParam(cricket::kCodecParamPort, sctp_port);
1684 LOG(INFO) << "AddSctpDataCodec: Got SCTP Port Number "
1685 << sctp_port;
1686 media_desc->AddCodec(codec_port);
1687 return true;
1688}
1689
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001690bool GetMinValue(const std::vector<int>& values, int* value) {
1691 if (values.empty()) {
1692 return false;
1693 }
1694 std::vector<int>::const_iterator found =
1695 std::min_element(values.begin(), values.end());
1696 *value = *found;
1697 return true;
1698}
1699
1700bool GetParameter(const std::string& name,
1701 const cricket::CodecParameterMap& params, int* value) {
1702 std::map<std::string, std::string>::const_iterator found =
1703 params.find(name);
1704 if (found == params.end()) {
1705 return false;
1706 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001707 if (!rtc::FromString(found->second, value)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001708 return false;
1709 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001710 return true;
1711}
1712
1713void BuildRtpMap(const MediaContentDescription* media_desc,
1714 const MediaType media_type,
1715 std::string* message) {
nisseede5da42017-01-12 05:15:36 -08001716 RTC_DCHECK(message != NULL);
1717 RTC_DCHECK(media_desc != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001718 std::ostringstream os;
1719 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
1720 const VideoContentDescription* video_desc =
1721 static_cast<const VideoContentDescription*>(media_desc);
1722 for (std::vector<cricket::VideoCodec>::const_iterator it =
1723 video_desc->codecs().begin();
1724 it != video_desc->codecs().end(); ++it) {
1725 // RFC 4566
1726 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1727 // [/<encodingparameters>]
1728 if (it->id != kWildcardPayloadType) {
1729 InitAttrLine(kAttributeRtpmap, &os);
1730 os << kSdpDelimiterColon << it->id << " " << it->name
1731 << "/" << kDefaultVideoClockrate;
1732 AddLine(os.str(), message);
1733 }
1734 AddRtcpFbLines(*it, message);
1735 AddFmtpLine(*it, message);
1736 }
1737 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
1738 const AudioContentDescription* audio_desc =
1739 static_cast<const AudioContentDescription*>(media_desc);
1740 std::vector<int> ptimes;
1741 std::vector<int> maxptimes;
1742 int max_minptime = 0;
1743 for (std::vector<cricket::AudioCodec>::const_iterator it =
1744 audio_desc->codecs().begin();
1745 it != audio_desc->codecs().end(); ++it) {
nisseede5da42017-01-12 05:15:36 -08001746 RTC_DCHECK(!it->name.empty());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001747 // RFC 4566
1748 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1749 // [/<encodingparameters>]
1750 InitAttrLine(kAttributeRtpmap, &os);
1751 os << kSdpDelimiterColon << it->id << " ";
1752 os << it->name << "/" << it->clockrate;
1753 if (it->channels != 1) {
1754 os << "/" << it->channels;
1755 }
1756 AddLine(os.str(), message);
1757 AddRtcpFbLines(*it, message);
1758 AddFmtpLine(*it, message);
1759 int minptime = 0;
1760 if (GetParameter(kCodecParamMinPTime, it->params, &minptime)) {
1761 max_minptime = std::max(minptime, max_minptime);
1762 }
1763 int ptime;
1764 if (GetParameter(kCodecParamPTime, it->params, &ptime)) {
1765 ptimes.push_back(ptime);
1766 }
1767 int maxptime;
1768 if (GetParameter(kCodecParamMaxPTime, it->params, &maxptime)) {
1769 maxptimes.push_back(maxptime);
1770 }
1771 }
1772 // Populate the maxptime attribute with the smallest maxptime of all codecs
1773 // under the same m-line.
1774 int min_maxptime = INT_MAX;
1775 if (GetMinValue(maxptimes, &min_maxptime)) {
1776 AddAttributeLine(kCodecParamMaxPTime, min_maxptime, message);
1777 }
nisseede5da42017-01-12 05:15:36 -08001778 RTC_DCHECK(min_maxptime > max_minptime);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001779 // Populate the ptime attribute with the smallest ptime or the largest
1780 // minptime, whichever is the largest, for all codecs under the same m-line.
1781 int ptime = INT_MAX;
1782 if (GetMinValue(ptimes, &ptime)) {
1783 ptime = std::min(ptime, min_maxptime);
1784 ptime = std::max(ptime, max_minptime);
1785 AddAttributeLine(kCodecParamPTime, ptime, message);
1786 }
1787 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
1788 const DataContentDescription* data_desc =
1789 static_cast<const DataContentDescription*>(media_desc);
1790 for (std::vector<cricket::DataCodec>::const_iterator it =
1791 data_desc->codecs().begin();
1792 it != data_desc->codecs().end(); ++it) {
1793 // RFC 4566
1794 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1795 // [/<encodingparameters>]
1796 InitAttrLine(kAttributeRtpmap, &os);
1797 os << kSdpDelimiterColon << it->id << " "
1798 << it->name << "/" << it->clockrate;
1799 AddLine(os.str(), message);
1800 }
1801 }
1802}
1803
1804void BuildCandidate(const std::vector<Candidate>& candidates,
honghaiza54a0802015-12-16 18:37:23 -08001805 bool include_ufrag,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001806 std::string* message) {
1807 std::ostringstream os;
1808
1809 for (std::vector<Candidate>::const_iterator it = candidates.begin();
1810 it != candidates.end(); ++it) {
1811 // RFC 5245
1812 // a=candidate:<foundation> <component-id> <transport> <priority>
1813 // <connection-address> <port> typ <candidate-types>
1814 // [raddr <connection-address>] [rport <port>]
1815 // *(SP extension-att-name SP extension-att-value)
1816 std::string type;
1817 // Map the cricket candidate type to "host" / "srflx" / "prflx" / "relay"
1818 if (it->type() == cricket::LOCAL_PORT_TYPE) {
1819 type = kCandidateHost;
1820 } else if (it->type() == cricket::STUN_PORT_TYPE) {
1821 type = kCandidateSrflx;
1822 } else if (it->type() == cricket::RELAY_PORT_TYPE) {
1823 type = kCandidateRelay;
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001824 } else if (it->type() == cricket::PRFLX_PORT_TYPE) {
1825 type = kCandidatePrflx;
1826 // Peer reflexive candidate may be signaled for being removed.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001827 } else {
nissec80e7412017-01-11 05:56:46 -08001828 RTC_NOTREACHED();
Peter Thatcher019087f2015-04-28 09:06:26 -07001829 // Never write out candidates if we don't know the type.
1830 continue;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001831 }
1832
1833 InitAttrLine(kAttributeCandidate, &os);
1834 os << kSdpDelimiterColon
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001835 << it->foundation() << " "
1836 << it->component() << " "
1837 << it->protocol() << " "
1838 << it->priority() << " "
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001839 << it->address().ipaddr().ToString() << " "
1840 << it->address().PortAsString() << " "
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001841 << kAttributeCandidateTyp << " "
1842 << type << " ";
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001843
1844 // Related address
1845 if (!it->related_address().IsNil()) {
1846 os << kAttributeCandidateRaddr << " "
1847 << it->related_address().ipaddr().ToString() << " "
1848 << kAttributeCandidateRport << " "
1849 << it->related_address().PortAsString() << " ";
1850 }
1851
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001852 if (it->protocol() == cricket::TCP_PROTOCOL_NAME) {
mallinath@webrtc.orge999bd02014-08-13 06:05:55 +00001853 os << kTcpCandidateType << " " << it->tcptype() << " ";
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001854 }
1855
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001856 // Extensions
1857 os << kAttributeCandidateGeneration << " " << it->generation();
honghaiza54a0802015-12-16 18:37:23 -08001858 if (include_ufrag && !it->username().empty()) {
1859 os << " " << kAttributeCandidateUfrag << " " << it->username();
1860 }
honghaiza0c44ea2016-03-23 16:07:48 -07001861 if (it->network_id() > 0) {
1862 os << " " << kAttributeCandidateNetworkId << " " << it->network_id();
1863 }
honghaize1a0c942016-02-16 14:54:56 -08001864 if (it->network_cost() > 0) {
1865 os << " " << kAttributeCandidateNetworkCost << " " << it->network_cost();
1866 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001867
1868 AddLine(os.str(), message);
1869 }
1870}
1871
1872void BuildIceOptions(const std::vector<std::string>& transport_options,
1873 std::string* message) {
1874 if (!transport_options.empty()) {
1875 std::ostringstream os;
1876 InitAttrLine(kAttributeIceOption, &os);
1877 os << kSdpDelimiterColon << transport_options[0];
1878 for (size_t i = 1; i < transport_options.size(); ++i) {
1879 os << kSdpDelimiterSpace << transport_options[i];
1880 }
1881 AddLine(os.str(), message);
1882 }
1883}
1884
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001885bool IsRtp(const std::string& protocol) {
1886 return protocol.empty() ||
1887 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
1888}
1889
1890bool IsDtlsSctp(const std::string& protocol) {
1891 // This intentionally excludes "SCTP" and "SCTP/DTLS".
lally@webrtc.orga7470932015-02-24 20:19:21 +00001892 return protocol.find(cricket::kMediaProtocolDtlsSctp) != std::string::npos;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001893}
1894
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001895bool ParseSessionDescription(const std::string& message, size_t* pos,
1896 std::string* session_id,
1897 std::string* session_version,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001898 TransportDescription* session_td,
1899 RtpHeaderExtensions* session_extmaps,
1900 cricket::SessionDescription* desc,
1901 SdpParseError* error) {
1902 std::string line;
1903
deadbeefc80741f2015-10-22 13:14:45 -07001904 desc->set_msid_supported(false);
1905
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001906 // RFC 4566
1907 // v= (protocol version)
1908 if (!GetLineWithType(message, pos, &line, kLineTypeVersion)) {
1909 return ParseFailedExpectLine(message, *pos, kLineTypeVersion,
1910 std::string(), error);
1911 }
1912 // RFC 4566
1913 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
1914 // <unicast-address>
1915 if (!GetLineWithType(message, pos, &line, kLineTypeOrigin)) {
1916 return ParseFailedExpectLine(message, *pos, kLineTypeOrigin,
1917 std::string(), error);
1918 }
1919 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001920 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001921 kSdpDelimiterSpace, &fields);
1922 const size_t expected_fields = 6;
1923 if (fields.size() != expected_fields) {
1924 return ParseFailedExpectFieldNum(line, expected_fields, error);
1925 }
1926 *session_id = fields[1];
1927 *session_version = fields[2];
1928
1929 // RFC 4566
1930 // s= (session name)
1931 if (!GetLineWithType(message, pos, &line, kLineTypeSessionName)) {
1932 return ParseFailedExpectLine(message, *pos, kLineTypeSessionName,
1933 std::string(), error);
1934 }
1935
1936 // Optional lines
1937 // Those are the optional lines, so shouldn't return false if not present.
1938 // RFC 4566
1939 // i=* (session information)
1940 GetLineWithType(message, pos, &line, kLineTypeSessionInfo);
1941
1942 // RFC 4566
1943 // u=* (URI of description)
1944 GetLineWithType(message, pos, &line, kLineTypeSessionUri);
1945
1946 // RFC 4566
1947 // e=* (email address)
1948 GetLineWithType(message, pos, &line, kLineTypeSessionEmail);
1949
1950 // RFC 4566
1951 // p=* (phone number)
1952 GetLineWithType(message, pos, &line, kLineTypeSessionPhone);
1953
1954 // RFC 4566
1955 // c=* (connection information -- not required if included in
1956 // all media)
1957 GetLineWithType(message, pos, &line, kLineTypeConnection);
1958
1959 // RFC 4566
1960 // b=* (zero or more bandwidth information lines)
1961 while (GetLineWithType(message, pos, &line, kLineTypeSessionBandwidth)) {
1962 // By pass zero or more b lines.
1963 }
1964
1965 // RFC 4566
1966 // One or more time descriptions ("t=" and "r=" lines; see below)
1967 // t= (time the session is active)
1968 // r=* (zero or more repeat times)
1969 // Ensure there's at least one time description
1970 if (!GetLineWithType(message, pos, &line, kLineTypeTiming)) {
1971 return ParseFailedExpectLine(message, *pos, kLineTypeTiming, std::string(),
1972 error);
1973 }
1974
1975 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
1976 // By pass zero or more r lines.
1977 }
1978
1979 // Go through the rest of the time descriptions
1980 while (GetLineWithType(message, pos, &line, kLineTypeTiming)) {
1981 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
1982 // By pass zero or more r lines.
1983 }
1984 }
1985
1986 // RFC 4566
1987 // z=* (time zone adjustments)
1988 GetLineWithType(message, pos, &line, kLineTypeTimeZone);
1989
1990 // RFC 4566
1991 // k=* (encryption key)
1992 GetLineWithType(message, pos, &line, kLineTypeEncryptionKey);
1993
1994 // RFC 4566
1995 // a=* (zero or more session attribute lines)
1996 while (GetLineWithType(message, pos, &line, kLineTypeAttributes)) {
1997 if (HasAttribute(line, kAttributeGroup)) {
1998 if (!ParseGroupAttribute(line, desc, error)) {
1999 return false;
2000 }
2001 } else if (HasAttribute(line, kAttributeIceUfrag)) {
2002 if (!GetValue(line, kAttributeIceUfrag,
2003 &(session_td->ice_ufrag), error)) {
2004 return false;
2005 }
2006 } else if (HasAttribute(line, kAttributeIcePwd)) {
2007 if (!GetValue(line, kAttributeIcePwd, &(session_td->ice_pwd), error)) {
2008 return false;
2009 }
2010 } else if (HasAttribute(line, kAttributeIceLite)) {
2011 session_td->ice_mode = cricket::ICEMODE_LITE;
2012 } else if (HasAttribute(line, kAttributeIceOption)) {
2013 if (!ParseIceOptions(line, &(session_td->transport_options), error)) {
2014 return false;
2015 }
2016 } else if (HasAttribute(line, kAttributeFingerprint)) {
2017 if (session_td->identity_fingerprint.get()) {
2018 return ParseFailed(
2019 line,
2020 "Can't have multiple fingerprint attributes at the same level.",
2021 error);
2022 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002023 rtc::SSLFingerprint* fingerprint = NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002024 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
2025 return false;
2026 }
2027 session_td->identity_fingerprint.reset(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002028 } else if (HasAttribute(line, kAttributeSetup)) {
2029 if (!ParseDtlsSetup(line, &(session_td->connection_role), error)) {
2030 return false;
2031 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002032 } else if (HasAttribute(line, kAttributeMsidSemantics)) {
2033 std::string semantics;
2034 if (!GetValue(line, kAttributeMsidSemantics, &semantics, error)) {
2035 return false;
2036 }
deadbeefc80741f2015-10-22 13:14:45 -07002037 desc->set_msid_supported(
2038 CaseInsensitiveFind(semantics, kMediaStreamSemantic));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002039 } else if (HasAttribute(line, kAttributeExtmap)) {
isheriff6f8d6862016-05-26 11:24:55 -07002040 RtpExtension extmap;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002041 if (!ParseExtmap(line, &extmap, error)) {
2042 return false;
2043 }
2044 session_extmaps->push_back(extmap);
2045 }
2046 }
2047
2048 return true;
2049}
2050
2051bool ParseGroupAttribute(const std::string& line,
2052 cricket::SessionDescription* desc,
2053 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08002054 RTC_DCHECK(desc != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002055
2056 // RFC 5888 and draft-holmberg-mmusic-sdp-bundle-negotiation-00
2057 // a=group:BUNDLE video voice
2058 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002059 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002060 kSdpDelimiterSpace, &fields);
2061 std::string semantics;
2062 if (!GetValue(fields[0], kAttributeGroup, &semantics, error)) {
2063 return false;
2064 }
2065 cricket::ContentGroup group(semantics);
2066 for (size_t i = 1; i < fields.size(); ++i) {
2067 group.AddContentName(fields[i]);
2068 }
2069 desc->AddGroup(group);
2070 return true;
2071}
2072
2073static bool ParseFingerprintAttribute(const std::string& line,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002074 rtc::SSLFingerprint** fingerprint,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002075 SdpParseError* error) {
2076 if (!IsLineType(line, kLineTypeAttributes) ||
2077 !HasAttribute(line, kAttributeFingerprint)) {
2078 return ParseFailedExpectLine(line, 0, kLineTypeAttributes,
2079 kAttributeFingerprint, error);
2080 }
2081
2082 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002083 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002084 kSdpDelimiterSpace, &fields);
2085 const size_t expected_fields = 2;
2086 if (fields.size() != expected_fields) {
2087 return ParseFailedExpectFieldNum(line, expected_fields, error);
2088 }
2089
2090 // The first field here is "fingerprint:<hash>.
2091 std::string algorithm;
2092 if (!GetValue(fields[0], kAttributeFingerprint, &algorithm, error)) {
2093 return false;
2094 }
2095
2096 // Downcase the algorithm. Note that we don't need to downcase the
2097 // fingerprint because hex_decode can handle upper-case.
2098 std::transform(algorithm.begin(), algorithm.end(), algorithm.begin(),
2099 ::tolower);
2100
2101 // The second field is the digest value. De-hexify it.
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002102 *fingerprint = rtc::SSLFingerprint::CreateFromRfc4572(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002103 algorithm, fields[1]);
2104 if (!*fingerprint) {
2105 return ParseFailed(line,
2106 "Failed to create fingerprint from the digest.",
2107 error);
2108 }
2109
2110 return true;
2111}
2112
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002113static bool ParseDtlsSetup(const std::string& line,
2114 cricket::ConnectionRole* role,
2115 SdpParseError* error) {
2116 // setup-attr = "a=setup:" role
2117 // role = "active" / "passive" / "actpass" / "holdconn"
2118 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002119 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColon, &fields);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002120 const size_t expected_fields = 2;
2121 if (fields.size() != expected_fields) {
2122 return ParseFailedExpectFieldNum(line, expected_fields, error);
2123 }
2124 std::string role_str = fields[1];
2125 if (!cricket::StringToConnectionRole(role_str, role)) {
2126 return ParseFailed(line, "Invalid attribute value.", error);
2127 }
2128 return true;
2129}
2130
deadbeef9d3584c2016-02-16 17:54:10 -08002131static bool ParseMsidAttribute(const std::string& line,
2132 std::string* stream_id,
2133 std::string* track_id,
2134 SdpParseError* error) {
2135 // draft-ietf-mmusic-msid-11
2136 // a=msid:<stream id> <track id>
2137 // msid-value = msid-id [ SP msid-appdata ]
2138 // msid-id = 1*64token-char ; see RFC 4566
2139 // msid-appdata = 1*64token-char ; see RFC 4566
2140 std::string field1;
2141 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
2142 &field1, track_id)) {
2143 const size_t expected_fields = 2;
2144 return ParseFailedExpectFieldNum(line, expected_fields, error);
2145 }
2146
2147 // msid:<msid-id>
2148 if (!GetValue(field1, kAttributeMsid, stream_id, error)) {
2149 return false;
2150 }
2151 return true;
2152}
2153
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002154// RFC 3551
2155// PT encoding media type clock rate channels
2156// name (Hz)
2157// 0 PCMU A 8,000 1
2158// 1 reserved A
2159// 2 reserved A
2160// 3 GSM A 8,000 1
2161// 4 G723 A 8,000 1
2162// 5 DVI4 A 8,000 1
2163// 6 DVI4 A 16,000 1
2164// 7 LPC A 8,000 1
2165// 8 PCMA A 8,000 1
2166// 9 G722 A 8,000 1
2167// 10 L16 A 44,100 2
2168// 11 L16 A 44,100 1
2169// 12 QCELP A 8,000 1
2170// 13 CN A 8,000 1
2171// 14 MPA A 90,000 (see text)
2172// 15 G728 A 8,000 1
2173// 16 DVI4 A 11,025 1
2174// 17 DVI4 A 22,050 1
2175// 18 G729 A 8,000 1
2176struct StaticPayloadAudioCodec {
2177 const char* name;
2178 int clockrate;
Peter Kasting69558702016-01-12 16:26:35 -08002179 size_t channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002180};
2181static const StaticPayloadAudioCodec kStaticPayloadAudioCodecs[] = {
2182 { "PCMU", 8000, 1 },
2183 { "reserved", 0, 0 },
2184 { "reserved", 0, 0 },
2185 { "GSM", 8000, 1 },
2186 { "G723", 8000, 1 },
2187 { "DVI4", 8000, 1 },
2188 { "DVI4", 16000, 1 },
2189 { "LPC", 8000, 1 },
2190 { "PCMA", 8000, 1 },
2191 { "G722", 8000, 1 },
2192 { "L16", 44100, 2 },
2193 { "L16", 44100, 1 },
2194 { "QCELP", 8000, 1 },
2195 { "CN", 8000, 1 },
2196 { "MPA", 90000, 1 },
2197 { "G728", 8000, 1 },
2198 { "DVI4", 11025, 1 },
2199 { "DVI4", 22050, 1 },
2200 { "G729", 8000, 1 },
2201};
2202
2203void MaybeCreateStaticPayloadAudioCodecs(
2204 const std::vector<int>& fmts, AudioContentDescription* media_desc) {
2205 if (!media_desc) {
2206 return;
2207 }
deadbeef67cf2c12016-04-13 10:07:16 -07002208 RTC_DCHECK(media_desc->codecs().empty());
solenberg9fa49752016-10-08 13:02:44 -07002209 for (int payload_type : fmts) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002210 if (!media_desc->HasCodec(payload_type) &&
2211 payload_type >= 0 &&
kjellander3e33bfe2016-06-20 07:04:09 -07002212 static_cast<uint32_t>(payload_type) <
2213 arraysize(kStaticPayloadAudioCodecs)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002214 std::string encoding_name = kStaticPayloadAudioCodecs[payload_type].name;
2215 int clock_rate = kStaticPayloadAudioCodecs[payload_type].clockrate;
Peter Kasting69558702016-01-12 16:26:35 -08002216 size_t channels = kStaticPayloadAudioCodecs[payload_type].channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002217 media_desc->AddCodec(cricket::AudioCodec(payload_type, encoding_name,
deadbeef67cf2c12016-04-13 10:07:16 -07002218 clock_rate, 0, channels));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002219 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002220 }
2221}
2222
2223template <class C>
2224static C* ParseContentDescription(const std::string& message,
2225 const MediaType media_type,
2226 int mline_index,
2227 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -07002228 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002229 size_t* pos,
2230 std::string* content_name,
deadbeef25ed4352016-12-12 18:37:36 -08002231 bool* bundle_only,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002232 TransportDescription* transport,
2233 std::vector<JsepIceCandidate*>* candidates,
2234 webrtc::SdpParseError* error) {
2235 C* media_desc = new C();
2236 switch (media_type) {
2237 case cricket::MEDIA_TYPE_AUDIO:
2238 *content_name = cricket::CN_AUDIO;
2239 break;
2240 case cricket::MEDIA_TYPE_VIDEO:
2241 *content_name = cricket::CN_VIDEO;
2242 break;
2243 case cricket::MEDIA_TYPE_DATA:
2244 *content_name = cricket::CN_DATA;
2245 break;
2246 default:
nissec80e7412017-01-11 05:56:46 -08002247 RTC_NOTREACHED();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002248 break;
2249 }
deadbeef67cf2c12016-04-13 10:07:16 -07002250 if (!ParseContent(message, media_type, mline_index, protocol, payload_types,
deadbeef25ed4352016-12-12 18:37:36 -08002251 pos, content_name, bundle_only, media_desc, transport,
2252 candidates, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002253 delete media_desc;
2254 return NULL;
2255 }
2256 // Sort the codecs according to the m-line fmt list.
deadbeef67cf2c12016-04-13 10:07:16 -07002257 std::unordered_map<int, int> payload_type_preferences;
2258 // "size + 1" so that the lowest preference payload type has a preference of
2259 // 1, which is greater than the default (0) for payload types not in the fmt
2260 // list.
2261 int preference = static_cast<int>(payload_types.size() + 1);
2262 for (int pt : payload_types) {
2263 payload_type_preferences[pt] = preference--;
2264 }
2265 std::vector<typename C::CodecType> codecs = media_desc->codecs();
2266 std::sort(codecs.begin(), codecs.end(), [&payload_type_preferences](
2267 const typename C::CodecType& a,
2268 const typename C::CodecType& b) {
2269 return payload_type_preferences[a.id] > payload_type_preferences[b.id];
2270 });
2271 media_desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002272 return media_desc;
2273}
2274
2275bool ParseMediaDescription(const std::string& message,
2276 const TransportDescription& session_td,
2277 const RtpHeaderExtensions& session_extmaps,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002278 size_t* pos,
2279 cricket::SessionDescription* desc,
2280 std::vector<JsepIceCandidate*>* candidates,
2281 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08002282 RTC_DCHECK(desc != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002283 std::string line;
2284 int mline_index = -1;
2285
2286 // Zero or more media descriptions
2287 // RFC 4566
2288 // m=<media> <port> <proto> <fmt>
2289 while (GetLineWithType(message, pos, &line, kLineTypeMedia)) {
2290 ++mline_index;
2291
2292 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002293 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002294 kSdpDelimiterSpace, &fields);
2295 const size_t expected_min_fields = 4;
2296 if (fields.size() < expected_min_fields) {
2297 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2298 }
deadbeef25ed4352016-12-12 18:37:36 -08002299 bool port_rejected = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002300 // RFC 3264
2301 // To reject an offered stream, the port number in the corresponding stream
2302 // in the answer MUST be set to zero.
2303 if (fields[1] == kMediaPortRejected) {
deadbeef25ed4352016-12-12 18:37:36 -08002304 port_rejected = true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002305 }
2306
2307 std::string protocol = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002308
2309 // <fmt>
deadbeef67cf2c12016-04-13 10:07:16 -07002310 std::vector<int> payload_types;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002311 if (IsRtp(protocol)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002312 for (size_t j = 3 ; j < fields.size(); ++j) {
2313 // TODO(wu): Remove when below bug is fixed.
2314 // https://bugzilla.mozilla.org/show_bug.cgi?id=996329
pbosbb36fdf2015-07-09 07:48:14 -07002315 if (fields[j].empty() && j == fields.size() - 1) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002316 continue;
2317 }
wu@webrtc.org36eda7c2014-04-15 20:37:30 +00002318
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002319 int pl = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00002320 if (!GetPayloadTypeFromString(line, fields[j], &pl, error)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002321 return false;
2322 }
deadbeef67cf2c12016-04-13 10:07:16 -07002323 payload_types.push_back(pl);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002324 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002325 }
2326
2327 // Make a temporary TransportDescription based on |session_td|.
2328 // Some of this gets overwritten by ParseContent.
deadbeef46eed762016-01-28 13:24:37 -08002329 TransportDescription transport(
2330 session_td.transport_options, session_td.ice_ufrag, session_td.ice_pwd,
2331 session_td.ice_mode, session_td.connection_role,
2332 session_td.identity_fingerprint.get());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002333
kwibergd1fe2812016-04-27 06:47:29 -07002334 std::unique_ptr<MediaContentDescription> content;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002335 std::string content_name;
deadbeef25ed4352016-12-12 18:37:36 -08002336 bool bundle_only = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002337 if (HasAttribute(line, kMediaTypeVideo)) {
2338 content.reset(ParseContentDescription<VideoContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002339 message, cricket::MEDIA_TYPE_VIDEO, mline_index, protocol,
deadbeef25ed4352016-12-12 18:37:36 -08002340 payload_types, pos, &content_name, &bundle_only, &transport,
2341 candidates, error));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002342 } else if (HasAttribute(line, kMediaTypeAudio)) {
2343 content.reset(ParseContentDescription<AudioContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002344 message, cricket::MEDIA_TYPE_AUDIO, mline_index, protocol,
deadbeef25ed4352016-12-12 18:37:36 -08002345 payload_types, pos, &content_name, &bundle_only, &transport,
2346 candidates, error));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002347 } else if (HasAttribute(line, kMediaTypeData)) {
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002348 DataContentDescription* data_desc =
wu@webrtc.org78187522013-10-07 23:32:02 +00002349 ParseContentDescription<DataContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002350 message, cricket::MEDIA_TYPE_DATA, mline_index, protocol,
deadbeef25ed4352016-12-12 18:37:36 -08002351 payload_types, pos, &content_name, &bundle_only, &transport,
2352 candidates, error);
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002353 content.reset(data_desc);
wu@webrtc.org78187522013-10-07 23:32:02 +00002354
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002355 int p;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002356 if (data_desc && IsDtlsSctp(protocol) && rtc::FromString(fields[3], &p)) {
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002357 if (!AddSctpDataCodec(data_desc, p))
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002358 return false;
wu@webrtc.org78187522013-10-07 23:32:02 +00002359 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002360 } else {
2361 LOG(LS_WARNING) << "Unsupported media type: " << line;
2362 continue;
2363 }
2364 if (!content.get()) {
2365 // ParseContentDescription returns NULL if failed.
2366 return false;
2367 }
2368
deadbeef25ed4352016-12-12 18:37:36 -08002369 bool content_rejected = false;
deadbeef12771a12017-01-03 13:53:47 -08002370 // A port of 0 is not interpreted as a rejected m= section when it's
2371 // used along with a=bundle-only.
deadbeef25ed4352016-12-12 18:37:36 -08002372 if (bundle_only) {
deadbeef25ed4352016-12-12 18:37:36 -08002373 if (!port_rejected) {
deadbeef12771a12017-01-03 13:53:47 -08002374 // Usage of bundle-only with a nonzero port is unspecified. So just
2375 // ignore bundle-only if we see this.
2376 bundle_only = false;
2377 LOG(LS_WARNING)
2378 << "a=bundle-only attribute observed with a nonzero "
2379 << "port; this usage is unspecified so the attribute is being "
2380 << "ignored.";
deadbeef25ed4352016-12-12 18:37:36 -08002381 }
2382 } else {
2383 // If not using bundle-only, interpret port 0 in the normal way; the m=
2384 // section is being rejected.
2385 content_rejected = port_rejected;
2386 }
2387
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002388 if (IsRtp(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002389 // Set the extmap.
2390 if (!session_extmaps.empty() &&
2391 !content->rtp_header_extensions().empty()) {
2392 return ParseFailed("",
2393 "The a=extmap MUST be either all session level or "
2394 "all media level.",
2395 error);
2396 }
2397 for (size_t i = 0; i < session_extmaps.size(); ++i) {
2398 content->AddRtpHeaderExtension(session_extmaps[i]);
2399 }
2400 }
2401 content->set_protocol(protocol);
2402 desc->AddContent(content_name,
deadbeef25ed4352016-12-12 18:37:36 -08002403 IsDtlsSctp(protocol) ? cricket::NS_JINGLE_DRAFT_SCTP
2404 : cricket::NS_JINGLE_RTP,
2405 content_rejected, bundle_only, content.release());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002406 // Create TransportInfo with the media level "ice-pwd" and "ice-ufrag".
2407 TransportInfo transport_info(content_name, transport);
2408
2409 if (!desc->AddTransportInfo(transport_info)) {
2410 std::ostringstream description;
2411 description << "Failed to AddTransportInfo with content name: "
2412 << content_name;
2413 return ParseFailed("", description.str(), error);
2414 }
2415 }
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00002416
2417 size_t end_of_message = message.size();
2418 if (mline_index == -1 && *pos != end_of_message) {
2419 ParseFailed(message, *pos, "Expects m line.", error);
2420 return false;
2421 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002422 return true;
2423}
2424
2425bool VerifyCodec(const cricket::Codec& codec) {
2426 // Codec has not been populated correctly unless the name has been set. This
2427 // can happen if an SDP has an fmtp or rtcp-fb with a payload type but doesn't
2428 // have a corresponding "rtpmap" line.
htab39db842016-12-08 01:50:48 -08002429 return !codec.name.empty();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002430}
2431
2432bool VerifyAudioCodecs(const AudioContentDescription* audio_desc) {
2433 const std::vector<cricket::AudioCodec>& codecs = audio_desc->codecs();
2434 for (std::vector<cricket::AudioCodec>::const_iterator iter = codecs.begin();
2435 iter != codecs.end(); ++iter) {
2436 if (!VerifyCodec(*iter)) {
2437 return false;
2438 }
2439 }
2440 return true;
2441}
2442
2443bool VerifyVideoCodecs(const VideoContentDescription* video_desc) {
2444 const std::vector<cricket::VideoCodec>& codecs = video_desc->codecs();
2445 for (std::vector<cricket::VideoCodec>::const_iterator iter = codecs.begin();
2446 iter != codecs.end(); ++iter) {
2447 if (!VerifyCodec(*iter)) {
2448 return false;
2449 }
2450 }
2451 return true;
2452}
2453
2454void AddParameters(const cricket::CodecParameterMap& parameters,
2455 cricket::Codec* codec) {
2456 for (cricket::CodecParameterMap::const_iterator iter =
2457 parameters.begin(); iter != parameters.end(); ++iter) {
2458 codec->SetParam(iter->first, iter->second);
2459 }
2460}
2461
2462void AddFeedbackParameter(const cricket::FeedbackParam& feedback_param,
2463 cricket::Codec* codec) {
2464 codec->AddFeedbackParam(feedback_param);
2465}
2466
2467void AddFeedbackParameters(const cricket::FeedbackParams& feedback_params,
2468 cricket::Codec* codec) {
2469 for (std::vector<cricket::FeedbackParam>::const_iterator iter =
2470 feedback_params.params().begin();
2471 iter != feedback_params.params().end(); ++iter) {
2472 codec->AddFeedbackParam(*iter);
2473 }
2474}
2475
2476// Gets the current codec setting associated with |payload_type|. If there
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002477// is no Codec associated with that payload type it returns an empty codec
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002478// with that payload type.
2479template <class T>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002480T GetCodecWithPayloadType(const std::vector<T>& codecs, int payload_type) {
magjedb05fa242016-11-11 04:00:16 -08002481 const T* codec = FindCodecById(codecs, payload_type);
2482 if (codec)
2483 return *codec;
2484 // Return empty codec with |payload_type|.
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002485 T ret_val;
magjedb05fa242016-11-11 04:00:16 -08002486 ret_val.id = payload_type;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002487 return ret_val;
2488}
2489
2490// Updates or creates a new codec entry in the audio description.
2491template <class T, class U>
2492void AddOrReplaceCodec(MediaContentDescription* content_desc, const U& codec) {
2493 T* desc = static_cast<T*>(content_desc);
2494 std::vector<U> codecs = desc->codecs();
2495 bool found = false;
2496
2497 typename std::vector<U>::iterator iter;
2498 for (iter = codecs.begin(); iter != codecs.end(); ++iter) {
2499 if (iter->id == codec.id) {
2500 *iter = codec;
2501 found = true;
2502 break;
2503 }
2504 }
2505 if (!found) {
2506 desc->AddCodec(codec);
2507 return;
2508 }
2509 desc->set_codecs(codecs);
2510}
2511
2512// Adds or updates existing codec corresponding to |payload_type| according
2513// to |parameters|.
2514template <class T, class U>
2515void UpdateCodec(MediaContentDescription* content_desc, int payload_type,
2516 const cricket::CodecParameterMap& parameters) {
2517 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002518 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2519 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002520 AddParameters(parameters, &new_codec);
2521 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2522}
2523
2524// Adds or updates existing codec corresponding to |payload_type| according
2525// to |feedback_param|.
2526template <class T, class U>
2527void UpdateCodec(MediaContentDescription* content_desc, int payload_type,
2528 const cricket::FeedbackParam& feedback_param) {
2529 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002530 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2531 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002532 AddFeedbackParameter(feedback_param, &new_codec);
2533 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2534}
2535
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002536template <class T>
2537bool PopWildcardCodec(std::vector<T>* codecs, T* wildcard_codec) {
2538 for (auto iter = codecs->begin(); iter != codecs->end(); ++iter) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002539 if (iter->id == kWildcardPayloadType) {
2540 *wildcard_codec = *iter;
2541 codecs->erase(iter);
2542 return true;
2543 }
2544 }
2545 return false;
2546}
2547
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002548template<class T>
2549void UpdateFromWildcardCodecs(cricket::MediaContentDescriptionImpl<T>* desc) {
2550 auto codecs = desc->codecs();
2551 T wildcard_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002552 if (!PopWildcardCodec(&codecs, &wildcard_codec)) {
2553 return;
2554 }
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002555 for (auto& codec : codecs) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002556 AddFeedbackParameters(wildcard_codec.feedback_params, &codec);
2557 }
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002558 desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002559}
2560
2561void AddAudioAttribute(const std::string& name, const std::string& value,
2562 AudioContentDescription* audio_desc) {
2563 if (value.empty()) {
2564 return;
2565 }
2566 std::vector<cricket::AudioCodec> codecs = audio_desc->codecs();
2567 for (std::vector<cricket::AudioCodec>::iterator iter = codecs.begin();
2568 iter != codecs.end(); ++iter) {
2569 iter->params[name] = value;
2570 }
2571 audio_desc->set_codecs(codecs);
2572}
2573
2574bool ParseContent(const std::string& message,
2575 const MediaType media_type,
2576 int mline_index,
2577 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -07002578 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002579 size_t* pos,
2580 std::string* content_name,
deadbeef25ed4352016-12-12 18:37:36 -08002581 bool* bundle_only,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002582 MediaContentDescription* media_desc,
2583 TransportDescription* transport,
2584 std::vector<JsepIceCandidate*>* candidates,
2585 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08002586 RTC_DCHECK(media_desc != NULL);
2587 RTC_DCHECK(content_name != NULL);
2588 RTC_DCHECK(transport != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002589
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +00002590 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
2591 MaybeCreateStaticPayloadAudioCodecs(
deadbeef67cf2c12016-04-13 10:07:16 -07002592 payload_types, static_cast<AudioContentDescription*>(media_desc));
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +00002593 }
2594
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002595 // The media level "ice-ufrag" and "ice-pwd".
2596 // The candidates before update the media level "ice-pwd" and "ice-ufrag".
2597 Candidates candidates_orig;
2598 std::string line;
2599 std::string mline_id;
2600 // Tracks created out of the ssrc attributes.
2601 StreamParamsVec tracks;
2602 SsrcInfoVec ssrc_infos;
2603 SsrcGroupVec ssrc_groups;
2604 std::string maxptime_as_string;
2605 std::string ptime_as_string;
deadbeef9d3584c2016-02-16 17:54:10 -08002606 std::string stream_id;
2607 std::string track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002608
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002609 // Loop until the next m line
2610 while (!IsLineType(message, kLineTypeMedia, *pos)) {
2611 if (!GetLine(message, pos, &line)) {
2612 if (*pos >= message.size()) {
2613 break; // Done parsing
2614 } else {
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +00002615 return ParseFailed(message, *pos, "Invalid SDP line.", error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002616 }
2617 }
2618
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002619 // RFC 4566
2620 // b=* (zero or more bandwidth information lines)
2621 if (IsLineType(line, kLineTypeSessionBandwidth)) {
2622 std::string bandwidth;
2623 if (HasAttribute(line, kApplicationSpecificMaximum)) {
2624 if (!GetValue(line, kApplicationSpecificMaximum, &bandwidth, error)) {
2625 return false;
2626 } else {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002627 int b = 0;
2628 if (!GetValueFromString(line, bandwidth, &b, error)) {
2629 return false;
2630 }
Peter Thatcherc0c3a862015-06-24 15:31:25 -07002631 // We should never use more than the default bandwidth for RTP-based
2632 // data channels. Don't allow SDP to set the bandwidth, because
2633 // that would give JS the opportunity to "break the Internet".
2634 // See: https://code.google.com/p/chromium/issues/detail?id=280726
2635 if (media_type == cricket::MEDIA_TYPE_DATA && IsRtp(protocol) &&
2636 b > cricket::kDataMaxBandwidth / 1000) {
2637 std::ostringstream description;
2638 description << "RTP-based data channels may not send more than "
2639 << cricket::kDataMaxBandwidth / 1000 << "kbps.";
2640 return ParseFailed(line, description.str(), error);
2641 }
deadbeefb2362572016-12-13 16:37:06 -08002642 // Prevent integer overflow.
2643 b = std::min(b, INT_MAX / 1000);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002644 media_desc->set_bandwidth(b * 1000);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002645 }
2646 }
2647 continue;
2648 }
2649
2650 if (!IsLineType(line, kLineTypeAttributes)) {
2651 // TODO: Handle other lines if needed.
2652 LOG(LS_INFO) << "Ignored line: " << line;
2653 continue;
2654 }
2655
2656 // Handle attributes common to SCTP and RTP.
2657 if (HasAttribute(line, kAttributeMid)) {
2658 // RFC 3388
2659 // mid-attribute = "a=mid:" identification-tag
2660 // identification-tag = token
2661 // Use the mid identification-tag as the content name.
2662 if (!GetValue(line, kAttributeMid, &mline_id, error)) {
2663 return false;
2664 }
2665 *content_name = mline_id;
deadbeef25ed4352016-12-12 18:37:36 -08002666 } else if (HasAttribute(line, kAttributeBundleOnly)) {
2667 *bundle_only = true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002668 } else if (HasAttribute(line, kAttributeCandidate)) {
2669 Candidate candidate;
2670 if (!ParseCandidate(line, &candidate, error, false)) {
2671 return false;
2672 }
deadbeef7bcdb692017-01-20 12:43:58 -08002673 // ParseCandidate will parse non-standard ufrag and password attributes,
2674 // since it's used for candidate trickling, but we only want to process
2675 // the "a=ice-ufrag"/"a=ice-pwd" values in a session description, so
2676 // strip them off at this point.
2677 candidate.set_username(std::string());
2678 candidate.set_password(std::string());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002679 candidates_orig.push_back(candidate);
2680 } else if (HasAttribute(line, kAttributeIceUfrag)) {
2681 if (!GetValue(line, kAttributeIceUfrag, &transport->ice_ufrag, error)) {
2682 return false;
2683 }
2684 } else if (HasAttribute(line, kAttributeIcePwd)) {
2685 if (!GetValue(line, kAttributeIcePwd, &transport->ice_pwd, error)) {
2686 return false;
2687 }
2688 } else if (HasAttribute(line, kAttributeIceOption)) {
2689 if (!ParseIceOptions(line, &transport->transport_options, error)) {
2690 return false;
2691 }
2692 } else if (HasAttribute(line, kAttributeFmtp)) {
2693 if (!ParseFmtpAttributes(line, media_type, media_desc, error)) {
2694 return false;
2695 }
2696 } else if (HasAttribute(line, kAttributeFingerprint)) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002697 rtc::SSLFingerprint* fingerprint = NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002698
2699 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
2700 return false;
2701 }
2702 transport->identity_fingerprint.reset(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002703 } else if (HasAttribute(line, kAttributeSetup)) {
2704 if (!ParseDtlsSetup(line, &(transport->connection_role), error)) {
2705 return false;
2706 }
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002707 } else if (IsDtlsSctp(protocol) && HasAttribute(line, kAttributeSctpPort)) {
deadbeef7e146cb2016-09-28 10:04:34 -07002708 if (media_type != cricket::MEDIA_TYPE_DATA) {
2709 return ParseFailed(
2710 line, "sctp-port attribute found in non-data media description.",
2711 error);
2712 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002713 int sctp_port;
2714 if (!ParseSctpPort(line, &sctp_port, error)) {
2715 return false;
2716 }
2717 if (!AddSctpDataCodec(static_cast<DataContentDescription*>(media_desc),
2718 sctp_port)) {
2719 return false;
2720 }
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002721 } else if (IsRtp(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002722 //
2723 // RTP specific attrubtes
2724 //
2725 if (HasAttribute(line, kAttributeRtcpMux)) {
2726 media_desc->set_rtcp_mux(true);
deadbeef13871492015-12-09 12:37:51 -08002727 } else if (HasAttribute(line, kAttributeRtcpReducedSize)) {
2728 media_desc->set_rtcp_reduced_size(true);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002729 } else if (HasAttribute(line, kAttributeSsrcGroup)) {
2730 if (!ParseSsrcGroupAttribute(line, &ssrc_groups, error)) {
2731 return false;
2732 }
2733 } else if (HasAttribute(line, kAttributeSsrc)) {
2734 if (!ParseSsrcAttribute(line, &ssrc_infos, error)) {
2735 return false;
2736 }
2737 } else if (HasAttribute(line, kAttributeCrypto)) {
2738 if (!ParseCryptoAttribute(line, media_desc, error)) {
2739 return false;
2740 }
2741 } else if (HasAttribute(line, kAttributeRtpmap)) {
deadbeef67cf2c12016-04-13 10:07:16 -07002742 if (!ParseRtpmapAttribute(line, media_type, payload_types, media_desc,
2743 error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002744 return false;
2745 }
2746 } else if (HasAttribute(line, kCodecParamMaxPTime)) {
2747 if (!GetValue(line, kCodecParamMaxPTime, &maxptime_as_string, error)) {
2748 return false;
2749 }
2750 } else if (HasAttribute(line, kAttributeRtcpFb)) {
2751 if (!ParseRtcpFbAttribute(line, media_type, media_desc, error)) {
2752 return false;
2753 }
2754 } else if (HasAttribute(line, kCodecParamPTime)) {
2755 if (!GetValue(line, kCodecParamPTime, &ptime_as_string, error)) {
2756 return false;
2757 }
2758 } else if (HasAttribute(line, kAttributeSendOnly)) {
2759 media_desc->set_direction(cricket::MD_SENDONLY);
2760 } else if (HasAttribute(line, kAttributeRecvOnly)) {
2761 media_desc->set_direction(cricket::MD_RECVONLY);
2762 } else if (HasAttribute(line, kAttributeInactive)) {
2763 media_desc->set_direction(cricket::MD_INACTIVE);
2764 } else if (HasAttribute(line, kAttributeSendRecv)) {
2765 media_desc->set_direction(cricket::MD_SENDRECV);
2766 } else if (HasAttribute(line, kAttributeExtmap)) {
isheriff6f8d6862016-05-26 11:24:55 -07002767 RtpExtension extmap;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002768 if (!ParseExtmap(line, &extmap, error)) {
2769 return false;
2770 }
2771 media_desc->AddRtpHeaderExtension(extmap);
2772 } else if (HasAttribute(line, kAttributeXGoogleFlag)) {
2773 // Experimental attribute. Conference mode activates more aggressive
2774 // AEC and NS settings.
2775 // TODO: expose API to set these directly.
2776 std::string flag_value;
2777 if (!GetValue(line, kAttributeXGoogleFlag, &flag_value, error)) {
2778 return false;
2779 }
2780 if (flag_value.compare(kValueConference) == 0)
2781 media_desc->set_conference_mode(true);
deadbeef9d3584c2016-02-16 17:54:10 -08002782 } else if (HasAttribute(line, kAttributeMsid)) {
2783 if (!ParseMsidAttribute(line, &stream_id, &track_id, error)) {
2784 return false;
2785 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002786 }
2787 } else {
2788 // Only parse lines that we are interested of.
2789 LOG(LS_INFO) << "Ignored line: " << line;
2790 continue;
2791 }
2792 }
2793
2794 // Create tracks from the |ssrc_infos|.
Taylor Brandstetter5de6b752016-03-09 17:02:30 -08002795 // If the stream_id/track_id for all SSRCS are identical, one StreamParams
2796 // will be created in CreateTracksFromSsrcInfos, containing all the SSRCs from
2797 // the m= section.
2798 CreateTracksFromSsrcInfos(ssrc_infos, stream_id, track_id, &tracks);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002799
2800 // Add the ssrc group to the track.
2801 for (SsrcGroupVec::iterator ssrc_group = ssrc_groups.begin();
2802 ssrc_group != ssrc_groups.end(); ++ssrc_group) {
2803 if (ssrc_group->ssrcs.empty()) {
2804 continue;
2805 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002806 uint32_t ssrc = ssrc_group->ssrcs.front();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002807 for (StreamParamsVec::iterator track = tracks.begin();
2808 track != tracks.end(); ++track) {
2809 if (track->has_ssrc(ssrc)) {
2810 track->ssrc_groups.push_back(*ssrc_group);
2811 }
2812 }
2813 }
2814
2815 // Add the new tracks to the |media_desc|.
deadbeef9d3584c2016-02-16 17:54:10 -08002816 for (StreamParams& track : tracks) {
2817 media_desc->AddStream(track);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002818 }
2819
2820 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
2821 AudioContentDescription* audio_desc =
2822 static_cast<AudioContentDescription*>(media_desc);
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002823 UpdateFromWildcardCodecs(audio_desc);
2824
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002825 // Verify audio codec ensures that no audio codec has been populated with
2826 // only fmtp.
2827 if (!VerifyAudioCodecs(audio_desc)) {
2828 return ParseFailed("Failed to parse audio codecs correctly.", error);
2829 }
2830 AddAudioAttribute(kCodecParamMaxPTime, maxptime_as_string, audio_desc);
2831 AddAudioAttribute(kCodecParamPTime, ptime_as_string, audio_desc);
2832 }
2833
2834 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002835 VideoContentDescription* video_desc =
2836 static_cast<VideoContentDescription*>(media_desc);
2837 UpdateFromWildcardCodecs(video_desc);
2838 // Verify video codec ensures that no video codec has been populated with
2839 // only rtcp-fb.
2840 if (!VerifyVideoCodecs(video_desc)) {
2841 return ParseFailed("Failed to parse video codecs correctly.", error);
2842 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002843 }
2844
2845 // RFC 5245
2846 // Update the candidates with the media level "ice-pwd" and "ice-ufrag".
2847 for (Candidates::iterator it = candidates_orig.begin();
2848 it != candidates_orig.end(); ++it) {
nisseede5da42017-01-12 05:15:36 -08002849 RTC_DCHECK((*it).username().empty() ||
2850 (*it).username() == transport->ice_ufrag);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002851 (*it).set_username(transport->ice_ufrag);
nisseede5da42017-01-12 05:15:36 -08002852 RTC_DCHECK((*it).password().empty());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002853 (*it).set_password(transport->ice_pwd);
2854 candidates->push_back(
2855 new JsepIceCandidate(mline_id, mline_index, *it));
2856 }
2857 return true;
2858}
2859
2860bool ParseSsrcAttribute(const std::string& line, SsrcInfoVec* ssrc_infos,
2861 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08002862 RTC_DCHECK(ssrc_infos != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002863 // RFC 5576
2864 // a=ssrc:<ssrc-id> <attribute>
2865 // a=ssrc:<ssrc-id> <attribute>:<value>
2866 std::string field1, field2;
Donald Curtis144d0182015-05-15 13:14:24 -07002867 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
2868 &field1, &field2)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002869 const size_t expected_fields = 2;
2870 return ParseFailedExpectFieldNum(line, expected_fields, error);
2871 }
2872
2873 // ssrc:<ssrc-id>
2874 std::string ssrc_id_s;
2875 if (!GetValue(field1, kAttributeSsrc, &ssrc_id_s, error)) {
2876 return false;
2877 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002878 uint32_t ssrc_id = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002879 if (!GetValueFromString(line, ssrc_id_s, &ssrc_id, error)) {
2880 return false;
2881 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002882
2883 std::string attribute;
2884 std::string value;
Donald Curtis144d0182015-05-15 13:14:24 -07002885 if (!rtc::tokenize_first(field2, kSdpDelimiterColon, &attribute, &value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002886 std::ostringstream description;
2887 description << "Failed to get the ssrc attribute value from " << field2
2888 << ". Expected format <attribute>:<value>.";
2889 return ParseFailed(line, description.str(), error);
2890 }
2891
2892 // Check if there's already an item for this |ssrc_id|. Create a new one if
2893 // there isn't.
2894 SsrcInfoVec::iterator ssrc_info = ssrc_infos->begin();
2895 for (; ssrc_info != ssrc_infos->end(); ++ssrc_info) {
2896 if (ssrc_info->ssrc_id == ssrc_id) {
2897 break;
2898 }
2899 }
2900 if (ssrc_info == ssrc_infos->end()) {
2901 SsrcInfo info;
2902 info.ssrc_id = ssrc_id;
2903 ssrc_infos->push_back(info);
2904 ssrc_info = ssrc_infos->end() - 1;
2905 }
2906
2907 // Store the info to the |ssrc_info|.
2908 if (attribute == kSsrcAttributeCname) {
2909 // RFC 5576
2910 // cname:<value>
2911 ssrc_info->cname = value;
2912 } else if (attribute == kSsrcAttributeMsid) {
2913 // draft-alvestrand-mmusic-msid-00
2914 // "msid:" identifier [ " " appdata ]
2915 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002916 rtc::split(value, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002917 if (fields.size() < 1 || fields.size() > 2) {
2918 return ParseFailed(line,
2919 "Expected format \"msid:<identifier>[ <appdata>]\".",
2920 error);
2921 }
deadbeef9d3584c2016-02-16 17:54:10 -08002922 ssrc_info->stream_id = fields[0];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002923 if (fields.size() == 2) {
deadbeef9d3584c2016-02-16 17:54:10 -08002924 ssrc_info->track_id = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002925 }
2926 } else if (attribute == kSsrcAttributeMslabel) {
2927 // draft-alvestrand-rtcweb-mid-01
2928 // mslabel:<value>
2929 ssrc_info->mslabel = value;
2930 } else if (attribute == kSSrcAttributeLabel) {
2931 // The label isn't defined.
2932 // label:<value>
2933 ssrc_info->label = value;
2934 }
2935 return true;
2936}
2937
2938bool ParseSsrcGroupAttribute(const std::string& line,
2939 SsrcGroupVec* ssrc_groups,
2940 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08002941 RTC_DCHECK(ssrc_groups != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002942 // RFC 5576
2943 // a=ssrc-group:<semantics> <ssrc-id> ...
2944 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002945 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002946 kSdpDelimiterSpace, &fields);
2947 const size_t expected_min_fields = 2;
2948 if (fields.size() < expected_min_fields) {
2949 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2950 }
2951 std::string semantics;
2952 if (!GetValue(fields[0], kAttributeSsrcGroup, &semantics, error)) {
2953 return false;
2954 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002955 std::vector<uint32_t> ssrcs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002956 for (size_t i = 1; i < fields.size(); ++i) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02002957 uint32_t ssrc = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002958 if (!GetValueFromString(line, fields[i], &ssrc, error)) {
2959 return false;
2960 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002961 ssrcs.push_back(ssrc);
2962 }
2963 ssrc_groups->push_back(SsrcGroup(semantics, ssrcs));
2964 return true;
2965}
2966
2967bool ParseCryptoAttribute(const std::string& line,
2968 MediaContentDescription* media_desc,
2969 SdpParseError* error) {
2970 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002971 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002972 kSdpDelimiterSpace, &fields);
2973 // RFC 4568
2974 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
2975 const size_t expected_min_fields = 3;
2976 if (fields.size() < expected_min_fields) {
2977 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2978 }
2979 std::string tag_value;
2980 if (!GetValue(fields[0], kAttributeCrypto, &tag_value, error)) {
2981 return false;
2982 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002983 int tag = 0;
2984 if (!GetValueFromString(line, tag_value, &tag, error)) {
2985 return false;
2986 }
jbauch083b73f2015-07-16 02:46:32 -07002987 const std::string& crypto_suite = fields[1];
2988 const std::string& key_params = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002989 std::string session_params;
2990 if (fields.size() > 3) {
2991 session_params = fields[3];
2992 }
2993 media_desc->AddCrypto(CryptoParams(tag, crypto_suite, key_params,
2994 session_params));
2995 return true;
2996}
2997
2998// Updates or creates a new codec entry in the audio description with according
deadbeef67cf2c12016-04-13 10:07:16 -07002999// to |name|, |clockrate|, |bitrate|, and |channels|.
3000void UpdateCodec(int payload_type,
3001 const std::string& name,
3002 int clockrate,
3003 int bitrate,
3004 size_t channels,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003005 AudioContentDescription* audio_desc) {
3006 // Codec may already be populated with (only) optional parameters
3007 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00003008 cricket::AudioCodec codec =
3009 GetCodecWithPayloadType(audio_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003010 codec.name = name;
3011 codec.clockrate = clockrate;
3012 codec.bitrate = bitrate;
3013 codec.channels = channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003014 AddOrReplaceCodec<AudioContentDescription, cricket::AudioCodec>(audio_desc,
3015 codec);
3016}
3017
3018// Updates or creates a new codec entry in the video description according to
deadbeef67cf2c12016-04-13 10:07:16 -07003019// |name|, |width|, |height|, and |framerate|.
3020void UpdateCodec(int payload_type,
3021 const std::string& name,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003022 VideoContentDescription* video_desc) {
3023 // Codec may already be populated with (only) optional parameters
3024 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00003025 cricket::VideoCodec codec =
3026 GetCodecWithPayloadType(video_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003027 codec.name = name;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003028 AddOrReplaceCodec<VideoContentDescription, cricket::VideoCodec>(video_desc,
3029 codec);
3030}
3031
3032bool ParseRtpmapAttribute(const std::string& line,
3033 const MediaType media_type,
deadbeef67cf2c12016-04-13 10:07:16 -07003034 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003035 MediaContentDescription* media_desc,
3036 SdpParseError* error) {
3037 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003038 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003039 kSdpDelimiterSpace, &fields);
3040 // RFC 4566
3041 // a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encodingparameters>]
3042 const size_t expected_min_fields = 2;
3043 if (fields.size() < expected_min_fields) {
3044 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
3045 }
3046 std::string payload_type_value;
3047 if (!GetValue(fields[0], kAttributeRtpmap, &payload_type_value, error)) {
3048 return false;
3049 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003050 int payload_type = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00003051 if (!GetPayloadTypeFromString(line, payload_type_value, &payload_type,
3052 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003053 return false;
3054 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003055
deadbeef67cf2c12016-04-13 10:07:16 -07003056 if (std::find(payload_types.begin(), payload_types.end(), payload_type) ==
3057 payload_types.end()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003058 LOG(LS_WARNING) << "Ignore rtpmap line that did not appear in the "
3059 << "<fmt> of the m-line: " << line;
3060 return true;
3061 }
jbauch083b73f2015-07-16 02:46:32 -07003062 const std::string& encoder = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003063 std::vector<std::string> codec_params;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003064 rtc::split(encoder, '/', &codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003065 // <encoding name>/<clock rate>[/<encodingparameters>]
3066 // 2 mandatory fields
3067 if (codec_params.size() < 2 || codec_params.size() > 3) {
3068 return ParseFailed(line,
3069 "Expected format \"<encoding name>/<clock rate>"
3070 "[/<encodingparameters>]\".",
3071 error);
3072 }
jbauch083b73f2015-07-16 02:46:32 -07003073 const std::string& encoding_name = codec_params[0];
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003074 int clock_rate = 0;
3075 if (!GetValueFromString(line, codec_params[1], &clock_rate, error)) {
3076 return false;
3077 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003078 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3079 VideoContentDescription* video_desc =
3080 static_cast<VideoContentDescription*>(media_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003081 UpdateCodec(payload_type, encoding_name,
deadbeef67cf2c12016-04-13 10:07:16 -07003082 video_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003083 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3084 // RFC 4566
3085 // For audio streams, <encoding parameters> indicates the number
3086 // of audio channels. This parameter is OPTIONAL and may be
3087 // omitted if the number of channels is one, provided that no
3088 // additional parameters are needed.
Peter Kasting69558702016-01-12 16:26:35 -08003089 size_t channels = 1;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003090 if (codec_params.size() == 3) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003091 if (!GetValueFromString(line, codec_params[2], &channels, error)) {
3092 return false;
3093 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003094 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003095 AudioContentDescription* audio_desc =
3096 static_cast<AudioContentDescription*>(media_desc);
ossue1405ad2017-01-23 08:55:48 -08003097 UpdateCodec(payload_type, encoding_name, clock_rate, 0, channels,
deadbeef67cf2c12016-04-13 10:07:16 -07003098 audio_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003099 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
3100 DataContentDescription* data_desc =
3101 static_cast<DataContentDescription*>(media_desc);
deadbeef67cf2c12016-04-13 10:07:16 -07003102 data_desc->AddCodec(cricket::DataCodec(payload_type, encoding_name));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003103 }
3104 return true;
3105}
3106
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003107bool ParseFmtpParam(const std::string& line, std::string* parameter,
3108 std::string* value, SdpParseError* error) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003109 if (!rtc::tokenize_first(line, kSdpDelimiterEqual, parameter, value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003110 ParseFailed(line, "Unable to parse fmtp parameter. \'=\' missing.", error);
3111 return false;
3112 }
3113 // a=fmtp:<payload_type> <param1>=<value1>; <param2>=<value2>; ...
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003114 return true;
3115}
3116
3117bool ParseFmtpAttributes(const std::string& line, const MediaType media_type,
3118 MediaContentDescription* media_desc,
3119 SdpParseError* error) {
3120 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3121 media_type != cricket::MEDIA_TYPE_VIDEO) {
3122 return true;
3123 }
Donald Curtis0e07f922015-05-15 09:21:23 -07003124
3125 std::string line_payload;
3126 std::string line_params;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003127
3128 // RFC 5576
3129 // a=fmtp:<format> <format specific parameters>
3130 // At least two fields, whereas the second one is any of the optional
3131 // parameters.
Donald Curtis144d0182015-05-15 13:14:24 -07003132 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
3133 &line_payload, &line_params)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003134 ParseFailedExpectMinFieldNum(line, 2, error);
3135 return false;
3136 }
3137
Donald Curtis0e07f922015-05-15 09:21:23 -07003138 // Parse out the payload information.
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003139 std::string payload_type_str;
Donald Curtis0e07f922015-05-15 09:21:23 -07003140 if (!GetValue(line_payload, kAttributeFmtp, &payload_type_str, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003141 return false;
3142 }
3143
Donald Curtis0e07f922015-05-15 09:21:23 -07003144 int payload_type = 0;
Donald Curtis144d0182015-05-15 13:14:24 -07003145 if (!GetPayloadTypeFromString(line_payload, payload_type_str, &payload_type,
3146 error)) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003147 return false;
3148 }
3149
3150 // Parse out format specific parameters.
3151 std::vector<std::string> fields;
3152 rtc::split(line_params, kSdpDelimiterSemicolon, &fields);
3153
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003154 cricket::CodecParameterMap codec_params;
Donald Curtis144d0182015-05-15 13:14:24 -07003155 for (auto& iter : fields) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003156 if (iter.find(kSdpDelimiterEqual) == std::string::npos) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003157 // Only fmtps with equals are currently supported. Other fmtp types
3158 // should be ignored. Unknown fmtps do not constitute an error.
3159 continue;
3160 }
Donald Curtis0e07f922015-05-15 09:21:23 -07003161
3162 std::string name;
3163 std::string value;
3164 if (!ParseFmtpParam(rtc::string_trim(iter), &name, &value, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003165 return false;
3166 }
3167 codec_params[name] = value;
3168 }
3169
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003170 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3171 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003172 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003173 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3174 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003175 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003176 }
3177 return true;
3178}
3179
3180bool ParseRtcpFbAttribute(const std::string& line, const MediaType media_type,
3181 MediaContentDescription* media_desc,
3182 SdpParseError* error) {
3183 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3184 media_type != cricket::MEDIA_TYPE_VIDEO) {
3185 return true;
3186 }
3187 std::vector<std::string> rtcp_fb_fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003188 rtc::split(line.c_str(), kSdpDelimiterSpace, &rtcp_fb_fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003189 if (rtcp_fb_fields.size() < 2) {
3190 return ParseFailedGetValue(line, kAttributeRtcpFb, error);
3191 }
3192 std::string payload_type_string;
3193 if (!GetValue(rtcp_fb_fields[0], kAttributeRtcpFb, &payload_type_string,
3194 error)) {
3195 return false;
3196 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003197 int payload_type = kWildcardPayloadType;
3198 if (payload_type_string != "*") {
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00003199 if (!GetPayloadTypeFromString(line, payload_type_string, &payload_type,
3200 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003201 return false;
3202 }
3203 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003204 std::string id = rtcp_fb_fields[1];
3205 std::string param = "";
3206 for (std::vector<std::string>::iterator iter = rtcp_fb_fields.begin() + 2;
3207 iter != rtcp_fb_fields.end(); ++iter) {
3208 param.append(*iter);
3209 }
3210 const cricket::FeedbackParam feedback_param(id, param);
3211
3212 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003213 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
3214 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003215 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003216 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
3217 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003218 }
3219 return true;
3220}
3221
3222} // namespace webrtc