blob: ced85cb323d7cf643427257c19d9b874a8c6f3c4 [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/common.h"
28#include "webrtc/base/logging.h"
29#include "webrtc/base/messagedigest.h"
30#include "webrtc/base/stringutils.h"
isheriff6f8d6862016-05-26 11:24:55 -070031// for RtpExtension
32#include "webrtc/config.h"
kjellandera96e2d72016-02-04 23:52:28 -080033#include "webrtc/media/base/codec.h"
kjellandera96e2d72016-02-04 23:52:28 -080034#include "webrtc/media/base/cryptoparams.h"
kjellanderf4752772016-03-02 05:42:30 -080035#include "webrtc/media/base/mediaconstants.h"
kjellandera96e2d72016-02-04 23:52:28 -080036#include "webrtc/media/base/rtputils.h"
deadbeef953c2ce2017-01-09 14:53:41 -080037#include "webrtc/media/sctp/sctptransportinternal.h"
kjellandera96e2d72016-02-04 23:52:28 -080038#include "webrtc/p2p/base/candidate.h"
kjellanderf4752772016-03-02 05:42:30 -080039#include "webrtc/p2p/base/p2pconstants.h"
kjellandera96e2d72016-02-04 23:52:28 -080040#include "webrtc/p2p/base/port.h"
kjellander@webrtc.org9b8df252016-02-12 06:47:59 +010041#include "webrtc/pc/mediasession.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000042
43using cricket::AudioContentDescription;
44using cricket::Candidate;
45using cricket::Candidates;
46using cricket::ContentDescription;
47using cricket::ContentInfo;
48using cricket::CryptoParams;
49using cricket::DataContentDescription;
50using cricket::ICE_CANDIDATE_COMPONENT_RTP;
51using cricket::ICE_CANDIDATE_COMPONENT_RTCP;
52using cricket::kCodecParamMaxBitrate;
53using cricket::kCodecParamMaxPTime;
54using cricket::kCodecParamMaxQuantization;
55using cricket::kCodecParamMinBitrate;
56using cricket::kCodecParamMinPTime;
57using cricket::kCodecParamPTime;
58using cricket::kCodecParamSPropStereo;
buildbot@webrtc.orged97bb02014-05-07 11:15:20 +000059using cricket::kCodecParamStartBitrate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000060using cricket::kCodecParamStereo;
61using cricket::kCodecParamUseInbandFec;
Minyue Li7100dcd2015-03-27 05:05:59 +010062using cricket::kCodecParamUseDtx;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000063using cricket::kCodecParamSctpProtocol;
64using cricket::kCodecParamSctpStreams;
henrike@webrtc.org1e09a712013-07-26 19:17:59 +000065using cricket::kCodecParamMaxAverageBitrate;
buildbot@webrtc.org5d639b32014-09-10 07:57:12 +000066using cricket::kCodecParamMaxPlaybackRate;
stefan@webrtc.org85d27942014-06-09 12:51:39 +000067using cricket::kCodecParamAssociatedPayloadType;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000068using cricket::MediaContentDescription;
69using cricket::MediaType;
isheriff6f8d6862016-05-26 11:24:55 -070070using cricket::RtpHeaderExtensions;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000071using cricket::SsrcGroup;
72using cricket::StreamParams;
73using cricket::StreamParamsVec;
74using cricket::TransportDescription;
75using cricket::TransportInfo;
76using cricket::VideoContentDescription;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000077using rtc::SocketAddress;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000078
henrike@webrtc.org28e20752013-07-10 00:45:36 +000079namespace cricket {
80class SessionDescription;
81}
82
83namespace webrtc {
84
85// Line type
86// RFC 4566
87// An SDP session description consists of a number of lines of text of
88// the form:
89// <type>=<value>
90// where <type> MUST be exactly one case-significant character.
deadbeef9d3584c2016-02-16 17:54:10 -080091static const int kLinePrefixLength = 2; // Length of <type>=
henrike@webrtc.org28e20752013-07-10 00:45:36 +000092static const char kLineTypeVersion = 'v';
93static const char kLineTypeOrigin = 'o';
94static const char kLineTypeSessionName = 's';
95static const char kLineTypeSessionInfo = 'i';
96static const char kLineTypeSessionUri = 'u';
97static const char kLineTypeSessionEmail = 'e';
98static const char kLineTypeSessionPhone = 'p';
99static const char kLineTypeSessionBandwidth = 'b';
100static const char kLineTypeTiming = 't';
101static const char kLineTypeRepeatTimes = 'r';
102static const char kLineTypeTimeZone = 'z';
103static const char kLineTypeEncryptionKey = 'k';
104static const char kLineTypeMedia = 'm';
105static const char kLineTypeConnection = 'c';
106static const char kLineTypeAttributes = 'a';
107
108// Attributes
109static const char kAttributeGroup[] = "group";
110static const char kAttributeMid[] = "mid";
deadbeef9d3584c2016-02-16 17:54:10 -0800111static const char kAttributeMsid[] = "msid";
deadbeef25ed4352016-12-12 18:37:36 -0800112static const char kAttributeBundleOnly[] = "bundle-only";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000113static const char kAttributeRtcpMux[] = "rtcp-mux";
deadbeef13871492015-12-09 12:37:51 -0800114static const char kAttributeRtcpReducedSize[] = "rtcp-rsize";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000115static const char kAttributeSsrc[] = "ssrc";
116static const char kSsrcAttributeCname[] = "cname";
117static const char kAttributeExtmap[] = "extmap";
118// draft-alvestrand-mmusic-msid-01
119// a=msid-semantic: WMS
120static const char kAttributeMsidSemantics[] = "msid-semantic";
121static const char kMediaStreamSemantic[] = "WMS";
122static const char kSsrcAttributeMsid[] = "msid";
123static const char kDefaultMsid[] = "default";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000124static const char kSsrcAttributeMslabel[] = "mslabel";
125static const char kSSrcAttributeLabel[] = "label";
126static const char kAttributeSsrcGroup[] = "ssrc-group";
127static const char kAttributeCrypto[] = "crypto";
128static const char kAttributeCandidate[] = "candidate";
129static const char kAttributeCandidateTyp[] = "typ";
130static const char kAttributeCandidateRaddr[] = "raddr";
131static const char kAttributeCandidateRport[] = "rport";
honghaiza54a0802015-12-16 18:37:23 -0800132static const char kAttributeCandidateUfrag[] = "ufrag";
133static const char kAttributeCandidatePwd[] = "pwd";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000134static const char kAttributeCandidateGeneration[] = "generation";
honghaiza0c44ea2016-03-23 16:07:48 -0700135static const char kAttributeCandidateNetworkId[] = "network-id";
honghaize1a0c942016-02-16 14:54:56 -0800136static const char kAttributeCandidateNetworkCost[] = "network-cost";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000137static const char kAttributeFingerprint[] = "fingerprint";
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000138static const char kAttributeSetup[] = "setup";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000139static const char kAttributeFmtp[] = "fmtp";
140static const char kAttributeRtpmap[] = "rtpmap";
wu@webrtc.org78187522013-10-07 23:32:02 +0000141static const char kAttributeSctpmap[] = "sctpmap";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000142static const char kAttributeRtcp[] = "rtcp";
143static const char kAttributeIceUfrag[] = "ice-ufrag";
144static const char kAttributeIcePwd[] = "ice-pwd";
145static const char kAttributeIceLite[] = "ice-lite";
146static const char kAttributeIceOption[] = "ice-options";
147static const char kAttributeSendOnly[] = "sendonly";
148static const char kAttributeRecvOnly[] = "recvonly";
149static const char kAttributeRtcpFb[] = "rtcp-fb";
150static const char kAttributeSendRecv[] = "sendrecv";
151static const char kAttributeInactive[] = "inactive";
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +0000152// draft-ietf-mmusic-sctp-sdp-07
153// a=sctp-port
154static const char kAttributeSctpPort[] = "sctp-port";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000155
156// Experimental flags
157static const char kAttributeXGoogleFlag[] = "x-google-flag";
158static const char kValueConference[] = "conference";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000159
160// Candidate
161static const char kCandidateHost[] = "host";
162static const char kCandidateSrflx[] = "srflx";
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700163static const char kCandidatePrflx[] = "prflx";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000164static const char kCandidateRelay[] = "relay";
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +0000165static const char kTcpCandidateType[] = "tcptype";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000166
167static const char kSdpDelimiterEqual = '=';
168static const char kSdpDelimiterSpace = ' ';
169static const char kSdpDelimiterColon = ':';
170static const char kSdpDelimiterSemicolon = ';';
171static const char kSdpDelimiterSlash = '/';
172static const char kNewLine = '\n';
173static const char kReturn = '\r';
174static const char kLineBreak[] = "\r\n";
175
176// TODO: Generate the Session and Time description
177// instead of hardcoding.
178static const char kSessionVersion[] = "v=0";
179// RFC 4566
180static const char kSessionOriginUsername[] = "-";
181static const char kSessionOriginSessionId[] = "0";
182static const char kSessionOriginSessionVersion[] = "0";
183static const char kSessionOriginNettype[] = "IN";
184static const char kSessionOriginAddrtype[] = "IP4";
185static const char kSessionOriginAddress[] = "127.0.0.1";
186static const char kSessionName[] = "s=-";
187static const char kTimeDescription[] = "t=0 0";
188static const char kAttrGroup[] = "a=group:BUNDLE";
189static const char kConnectionNettype[] = "IN";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000190static const char kConnectionIpv4Addrtype[] = "IP4";
191static const char kConnectionIpv6Addrtype[] = "IP6";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000192static const char kMediaTypeVideo[] = "video";
193static const char kMediaTypeAudio[] = "audio";
194static const char kMediaTypeData[] = "application";
195static const char kMediaPortRejected[] = "0";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000196// draft-ietf-mmusic-trickle-ice-01
197// When no candidates have been gathered, set the connection
198// address to IP6 ::.
perkj@webrtc.orgd105cc82014-11-07 11:22:06 +0000199// TODO(perkj): FF can not parse IP6 ::. See http://crbug/430333
200// Use IPV4 per default.
201static const char kDummyAddress[] = "0.0.0.0";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000202static const char kDummyPort[] = "9";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000203// RFC 3556
204static const char kApplicationSpecificMaximum[] = "AS";
205
206static const int kDefaultVideoClockrate = 90000;
207
wu@webrtc.org78187522013-10-07 23:32:02 +0000208static const char kDefaultSctpmapProtocol[] = "webrtc-datachannel";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000209
pkasting@chromium.orgd3245462015-02-23 21:28:22 +0000210// RTP payload type is in the 0-127 range. Use -1 to indicate "all" payload
211// types.
212const int kWildcardPayloadType = -1;
213
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000214struct SsrcInfo {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200215 uint32_t ssrc_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000216 std::string cname;
deadbeef9d3584c2016-02-16 17:54:10 -0800217 std::string stream_id;
218 std::string track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000219
220 // For backward compatibility.
221 // TODO(ronghuawu): Remove below 2 fields once all the clients support msid.
222 std::string label;
223 std::string mslabel;
224};
225typedef std::vector<SsrcInfo> SsrcInfoVec;
226typedef std::vector<SsrcGroup> SsrcGroupVec;
227
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000228template <class T>
229static void AddFmtpLine(const T& codec, std::string* message);
230static void BuildMediaDescription(const ContentInfo* content_info,
231 const TransportInfo* transport_info,
232 const MediaType media_type,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000233 const std::vector<Candidate>& candidates,
deadbeef9d3584c2016-02-16 17:54:10 -0800234 bool unified_plan_sdp,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000235 std::string* message);
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000236static void BuildSctpContentAttributes(std::string* message, int sctp_port);
deadbeef9d3584c2016-02-16 17:54:10 -0800237static void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
238 const MediaType media_type,
239 bool unified_plan_sdp,
240 std::string* message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000241static void BuildRtpMap(const MediaContentDescription* media_desc,
242 const MediaType media_type,
243 std::string* message);
244static void BuildCandidate(const std::vector<Candidate>& candidates,
honghaiza54a0802015-12-16 18:37:23 -0800245 bool include_ufrag,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000246 std::string* message);
247static void BuildIceOptions(const std::vector<std::string>& transport_options,
248 std::string* message);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +0000249static bool IsRtp(const std::string& protocol);
250static bool IsDtlsSctp(const std::string& protocol);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000251static bool ParseSessionDescription(const std::string& message, size_t* pos,
252 std::string* session_id,
253 std::string* session_version,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000254 TransportDescription* session_td,
255 RtpHeaderExtensions* session_extmaps,
256 cricket::SessionDescription* desc,
257 SdpParseError* error);
258static bool ParseGroupAttribute(const std::string& line,
259 cricket::SessionDescription* desc,
260 SdpParseError* error);
261static bool ParseMediaDescription(
262 const std::string& message,
263 const TransportDescription& session_td,
264 const RtpHeaderExtensions& session_extmaps,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000265 size_t* pos, cricket::SessionDescription* desc,
266 std::vector<JsepIceCandidate*>* candidates,
267 SdpParseError* error);
268static bool ParseContent(const std::string& message,
269 const MediaType media_type,
270 int mline_index,
271 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -0700272 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000273 size_t* pos,
274 std::string* content_name,
deadbeef25ed4352016-12-12 18:37:36 -0800275 bool* bundle_only,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000276 MediaContentDescription* media_desc,
277 TransportDescription* transport,
278 std::vector<JsepIceCandidate*>* candidates,
279 SdpParseError* error);
280static bool ParseSsrcAttribute(const std::string& line,
281 SsrcInfoVec* ssrc_infos,
282 SdpParseError* error);
283static bool ParseSsrcGroupAttribute(const std::string& line,
284 SsrcGroupVec* ssrc_groups,
285 SdpParseError* error);
286static bool ParseCryptoAttribute(const std::string& line,
287 MediaContentDescription* media_desc,
288 SdpParseError* error);
289static bool ParseRtpmapAttribute(const std::string& line,
290 const MediaType media_type,
deadbeef67cf2c12016-04-13 10:07:16 -0700291 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000292 MediaContentDescription* media_desc,
293 SdpParseError* error);
294static bool ParseFmtpAttributes(const std::string& line,
295 const MediaType media_type,
296 MediaContentDescription* media_desc,
297 SdpParseError* error);
298static bool ParseFmtpParam(const std::string& line, std::string* parameter,
299 std::string* value, SdpParseError* error);
300static bool ParseCandidate(const std::string& message, Candidate* candidate,
301 SdpParseError* error, bool is_raw);
302static bool ParseRtcpFbAttribute(const std::string& line,
303 const MediaType media_type,
304 MediaContentDescription* media_desc,
305 SdpParseError* error);
306static bool ParseIceOptions(const std::string& line,
307 std::vector<std::string>* transport_options,
308 SdpParseError* error);
309static bool ParseExtmap(const std::string& line,
isheriff6f8d6862016-05-26 11:24:55 -0700310 RtpExtension* extmap,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000311 SdpParseError* error);
312static bool ParseFingerprintAttribute(const std::string& line,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000313 rtc::SSLFingerprint** fingerprint,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000314 SdpParseError* error);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000315static bool ParseDtlsSetup(const std::string& line,
316 cricket::ConnectionRole* role,
317 SdpParseError* error);
deadbeef9d3584c2016-02-16 17:54:10 -0800318static bool ParseMsidAttribute(const std::string& line,
319 std::string* stream_id,
320 std::string* track_id,
321 SdpParseError* error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000322
323// Helper functions
324
325// Below ParseFailed*** functions output the line that caused the parsing
326// failure and the detailed reason (|description|) of the failure to |error|.
327// The functions always return false so that they can be used directly in the
328// following way when error happens:
329// "return ParseFailed***(...);"
330
331// The line starting at |line_start| of |message| is the failing line.
332// The reason for the failure should be provided in the |description|.
333// An example of a description could be "unknown character".
334static bool ParseFailed(const std::string& message,
335 size_t line_start,
336 const std::string& description,
337 SdpParseError* error) {
338 // Get the first line of |message| from |line_start|.
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000339 std::string first_line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000340 size_t line_end = message.find(kNewLine, line_start);
341 if (line_end != std::string::npos) {
342 if (line_end > 0 && (message.at(line_end - 1) == kReturn)) {
343 --line_end;
344 }
345 first_line = message.substr(line_start, (line_end - line_start));
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000346 } else {
347 first_line = message.substr(line_start);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000348 }
349
350 if (error) {
351 error->line = first_line;
352 error->description = description;
353 }
354 LOG(LS_ERROR) << "Failed to parse: \"" << first_line
355 << "\". Reason: " << description;
356 return false;
357}
358
359// |line| is the failing line. The reason for the failure should be
360// provided in the |description|.
361static bool ParseFailed(const std::string& line,
362 const std::string& description,
363 SdpParseError* error) {
364 return ParseFailed(line, 0, description, error);
365}
366
367// Parses failure where the failing SDP line isn't know or there are multiple
368// failing lines.
369static bool ParseFailed(const std::string& description,
370 SdpParseError* error) {
371 return ParseFailed("", description, error);
372}
373
374// |line| is the failing line. The failure is due to the fact that |line|
375// doesn't have |expected_fields| fields.
376static bool ParseFailedExpectFieldNum(const std::string& line,
377 int expected_fields,
378 SdpParseError* error) {
379 std::ostringstream description;
380 description << "Expects " << expected_fields << " fields.";
381 return ParseFailed(line, description.str(), error);
382}
383
384// |line| is the failing line. The failure is due to the fact that |line| has
385// less than |expected_min_fields| fields.
386static bool ParseFailedExpectMinFieldNum(const std::string& line,
387 int expected_min_fields,
388 SdpParseError* error) {
389 std::ostringstream description;
390 description << "Expects at least " << expected_min_fields << " fields.";
391 return ParseFailed(line, description.str(), error);
392}
393
394// |line| is the failing line. The failure is due to the fact that it failed to
395// get the value of |attribute|.
396static bool ParseFailedGetValue(const std::string& line,
397 const std::string& attribute,
398 SdpParseError* error) {
399 std::ostringstream description;
400 description << "Failed to get the value of attribute: " << attribute;
401 return ParseFailed(line, description.str(), error);
402}
403
404// The line starting at |line_start| of |message| is the failing line. The
405// failure is due to the line type (e.g. the "m" part of the "m-line")
406// not matching what is expected. The expected line type should be
407// provided as |line_type|.
408static bool ParseFailedExpectLine(const std::string& message,
409 size_t line_start,
410 const char line_type,
411 const std::string& line_value,
412 SdpParseError* error) {
413 std::ostringstream description;
414 description << "Expect line: " << line_type << "=" << line_value;
415 return ParseFailed(message, line_start, description.str(), error);
416}
417
418static bool AddLine(const std::string& line, std::string* message) {
419 if (!message)
420 return false;
421
422 message->append(line);
423 message->append(kLineBreak);
424 return true;
425}
426
427static bool GetLine(const std::string& message,
428 size_t* pos,
429 std::string* line) {
430 size_t line_begin = *pos;
431 size_t line_end = message.find(kNewLine, line_begin);
432 if (line_end == std::string::npos) {
433 return false;
434 }
435 // Update the new start position
436 *pos = line_end + 1;
437 if (line_end > 0 && (message.at(line_end - 1) == kReturn)) {
438 --line_end;
439 }
440 *line = message.substr(line_begin, (line_end - line_begin));
441 const char* cline = line->c_str();
442 // RFC 4566
443 // An SDP session description consists of a number of lines of text of
444 // the form:
445 // <type>=<value>
446 // where <type> MUST be exactly one case-significant character and
447 // <value> is structured text whose format depends on <type>.
448 // Whitespace MUST NOT be used on either side of the "=" sign.
decurtis@webrtc.org8af11042015-01-07 19:15:51 +0000449 if (line->length() < 3 ||
450 !islower(cline[0]) ||
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000451 cline[1] != kSdpDelimiterEqual ||
452 cline[2] == kSdpDelimiterSpace) {
453 *pos = line_begin;
454 return false;
455 }
456 return true;
457}
458
459// Init |os| to "|type|=|value|".
460static void InitLine(const char type,
461 const std::string& value,
462 std::ostringstream* os) {
463 os->str("");
464 *os << type << kSdpDelimiterEqual << value;
465}
466
467// Init |os| to "a=|attribute|".
468static void InitAttrLine(const std::string& attribute, std::ostringstream* os) {
469 InitLine(kLineTypeAttributes, attribute, os);
470}
471
472// Writes a SDP attribute line based on |attribute| and |value| to |message|.
473static void AddAttributeLine(const std::string& attribute, int value,
474 std::string* message) {
475 std::ostringstream os;
476 InitAttrLine(attribute, &os);
477 os << kSdpDelimiterColon << value;
478 AddLine(os.str(), message);
479}
480
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000481static bool IsLineType(const std::string& message,
482 const char type,
483 size_t line_start) {
484 if (message.size() < line_start + kLinePrefixLength) {
485 return false;
486 }
487 const char* cmessage = message.c_str();
488 return (cmessage[line_start] == type &&
489 cmessage[line_start + 1] == kSdpDelimiterEqual);
490}
491
492static bool IsLineType(const std::string& line,
493 const char type) {
494 return IsLineType(line, type, 0);
495}
496
497static bool GetLineWithType(const std::string& message, size_t* pos,
498 std::string* line, const char type) {
499 if (!IsLineType(message, type, *pos)) {
500 return false;
501 }
502
503 if (!GetLine(message, pos, line))
504 return false;
505
506 return true;
507}
508
509static bool HasAttribute(const std::string& line,
510 const std::string& attribute) {
511 return (line.compare(kLinePrefixLength, attribute.size(), attribute) == 0);
512}
513
Peter Boström0c4e06b2015-10-07 12:23:21 +0200514static bool AddSsrcLine(uint32_t ssrc_id,
515 const std::string& attribute,
516 const std::string& value,
517 std::string* message) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000518 // RFC 5576
519 // a=ssrc:<ssrc-id> <attribute>:<value>
520 std::ostringstream os;
521 InitAttrLine(kAttributeSsrc, &os);
522 os << kSdpDelimiterColon << ssrc_id << kSdpDelimiterSpace
523 << attribute << kSdpDelimiterColon << value;
524 return AddLine(os.str(), message);
525}
526
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000527// Get value only from <attribute>:<value>.
528static bool GetValue(const std::string& message, const std::string& attribute,
529 std::string* value, SdpParseError* error) {
530 std::string leftpart;
Donald Curtis0e07f922015-05-15 09:21:23 -0700531 if (!rtc::tokenize_first(message, kSdpDelimiterColon, &leftpart, value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000532 return ParseFailedGetValue(message, attribute, error);
533 }
534 // The left part should end with the expected attribute.
535 if (leftpart.length() < attribute.length() ||
536 leftpart.compare(leftpart.length() - attribute.length(),
537 attribute.length(), attribute) != 0) {
538 return ParseFailedGetValue(message, attribute, error);
539 }
540 return true;
541}
542
543static bool CaseInsensitiveFind(std::string str1, std::string str2) {
544 std::transform(str1.begin(), str1.end(), str1.begin(),
545 ::tolower);
546 std::transform(str2.begin(), str2.end(), str2.begin(),
547 ::tolower);
548 return str1.find(str2) != std::string::npos;
549}
550
wu@webrtc.org5e760e72014-04-02 23:19:09 +0000551template <class T>
552static bool GetValueFromString(const std::string& line,
553 const std::string& s,
554 T* t,
555 SdpParseError* error) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000556 if (!rtc::FromString(s, t)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +0000557 std::ostringstream description;
558 description << "Invalid value: " << s << ".";
559 return ParseFailed(line, description.str(), error);
560 }
561 return true;
562}
563
pkasting@chromium.orge9facf82015-02-17 20:36:28 +0000564static bool GetPayloadTypeFromString(const std::string& line,
565 const std::string& s,
566 int* payload_type,
567 SdpParseError* error) {
568 return GetValueFromString(line, s, payload_type, error) &&
569 cricket::IsValidRtpPayloadType(*payload_type);
570}
571
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800572// |msid_stream_id| and |msid_track_id| represent the stream/track ID from the
573// "a=msid" attribute, if it exists. They are empty if the attribute does not
574// exist.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000575void CreateTracksFromSsrcInfos(const SsrcInfoVec& ssrc_infos,
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800576 const std::string& msid_stream_id,
577 const std::string& msid_track_id,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000578 StreamParamsVec* tracks) {
nisseede5da42017-01-12 05:15:36 -0800579 RTC_DCHECK(tracks != NULL);
580 RTC_DCHECK(msid_stream_id.empty() == msid_track_id.empty());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000581 for (SsrcInfoVec::const_iterator ssrc_info = ssrc_infos.begin();
582 ssrc_info != ssrc_infos.end(); ++ssrc_info) {
583 if (ssrc_info->cname.empty()) {
584 continue;
585 }
586
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800587 std::string stream_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000588 std::string track_id;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800589 if (ssrc_info->stream_id.empty() && !ssrc_info->mslabel.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000590 // If there's no msid and there's mslabel, we consider this is a sdp from
591 // a older version of client that doesn't support msid.
592 // In that case, we use the mslabel and label to construct the track.
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800593 stream_id = ssrc_info->mslabel;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000594 track_id = ssrc_info->label;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800595 } else if (ssrc_info->stream_id.empty() && !msid_stream_id.empty()) {
596 // If there's no msid in the SSRC attributes, but there's a global one
597 // (from a=msid), use that. This is the case with unified plan SDP.
598 stream_id = msid_stream_id;
599 track_id = msid_track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000600 } else {
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800601 stream_id = ssrc_info->stream_id;
deadbeef9d3584c2016-02-16 17:54:10 -0800602 track_id = ssrc_info->track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000603 }
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800604 // If a stream/track ID wasn't populated from the SSRC attributes OR the
605 // msid attribute, use default/random values.
606 if (stream_id.empty()) {
607 stream_id = kDefaultMsid;
608 }
609 if (track_id.empty()) {
610 // TODO(ronghuawu): What should we do if the track id doesn't appear?
611 // Create random string (which will be used as track label later)?
612 track_id = rtc::CreateRandomString(8);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000613 }
614
615 StreamParamsVec::iterator track = tracks->begin();
616 for (; track != tracks->end(); ++track) {
617 if (track->id == track_id) {
618 break;
619 }
620 }
621 if (track == tracks->end()) {
622 // If we don't find an existing track, create a new one.
623 tracks->push_back(StreamParams());
624 track = tracks->end() - 1;
625 }
626 track->add_ssrc(ssrc_info->ssrc_id);
627 track->cname = ssrc_info->cname;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800628 track->sync_label = stream_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000629 track->id = track_id;
630 }
631}
632
633void GetMediaStreamLabels(const ContentInfo* content,
634 std::set<std::string>* labels) {
635 const MediaContentDescription* media_desc =
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000636 static_cast<const MediaContentDescription*>(
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000637 content->description);
638 const cricket::StreamParamsVec& streams = media_desc->streams();
639 for (cricket::StreamParamsVec::const_iterator it = streams.begin();
640 it != streams.end(); ++it) {
641 labels->insert(it->sync_label);
642 }
643}
644
645// RFC 5245
646// It is RECOMMENDED that default candidates be chosen based on the
647// likelihood of those candidates to work with the peer that is being
648// contacted. It is RECOMMENDED that relayed > reflexive > host.
649static const int kPreferenceUnknown = 0;
650static const int kPreferenceHost = 1;
651static const int kPreferenceReflexive = 2;
652static const int kPreferenceRelayed = 3;
653
654static int GetCandidatePreferenceFromType(const std::string& type) {
655 int preference = kPreferenceUnknown;
656 if (type == cricket::LOCAL_PORT_TYPE) {
657 preference = kPreferenceHost;
658 } else if (type == cricket::STUN_PORT_TYPE) {
659 preference = kPreferenceReflexive;
660 } else if (type == cricket::RELAY_PORT_TYPE) {
661 preference = kPreferenceRelayed;
662 } else {
nissec80e7412017-01-11 05:56:46 -0800663 RTC_NOTREACHED();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000664 }
665 return preference;
666}
667
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000668// Get ip and port of the default destination from the |candidates| with the
669// given value of |component_id|. The default candidate should be the one most
670// likely to work, typically IPv4 relay.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000671// RFC 5245
672// The value of |component_id| currently supported are 1 (RTP) and 2 (RTCP).
673// TODO: Decide the default destination in webrtcsession and
674// pass it down via SessionDescription.
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000675static void GetDefaultDestination(
676 const std::vector<Candidate>& candidates,
677 int component_id, std::string* port,
678 std::string* ip, std::string* addr_type) {
perkj@webrtc.orgd105cc82014-11-07 11:22:06 +0000679 *addr_type = kConnectionIpv4Addrtype;
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000680 *port = kDummyPort;
681 *ip = kDummyAddress;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000682 int current_preference = kPreferenceUnknown;
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000683 int current_family = AF_UNSPEC;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000684 for (std::vector<Candidate>::const_iterator it = candidates.begin();
685 it != candidates.end(); ++it) {
686 if (it->component() != component_id) {
687 continue;
688 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000689 // Default destination should be UDP only.
690 if (it->protocol() != cricket::UDP_PROTOCOL_NAME) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000691 continue;
692 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000693 const int preference = GetCandidatePreferenceFromType(it->type());
694 const int family = it->address().ipaddr().family();
695 // See if this candidate is more preferable then the current one if it's the
696 // same family. Or if the current family is IPv4 already so we could safely
697 // ignore all IPv6 ones. WebRTC bug 4269.
698 // http://code.google.com/p/webrtc/issues/detail?id=4269
699 if ((preference <= current_preference && current_family == family) ||
700 (current_family == AF_INET && family == AF_INET6)) {
701 continue;
702 }
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000703 if (family == AF_INET) {
704 addr_type->assign(kConnectionIpv4Addrtype);
705 } else if (family == AF_INET6) {
706 addr_type->assign(kConnectionIpv6Addrtype);
707 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000708 current_preference = preference;
709 current_family = family;
710 *port = it->address().PortAsString();
711 *ip = it->address().ipaddr().ToString();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000712 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000713}
714
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000715// Update |mline|'s default destination and append a c line after it.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000716static void UpdateMediaDefaultDestination(
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000717 const std::vector<Candidate>& candidates,
jbauch083b73f2015-07-16 02:46:32 -0700718 const std::string& mline,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000719 std::string* message) {
720 std::string new_lines;
721 AddLine(mline, &new_lines);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000722 // RFC 4566
723 // m=<media> <port> <proto> <fmt> ...
724 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000725 rtc::split(mline, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000726 if (fields.size() < 3) {
727 return;
728 }
729
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000730 std::ostringstream os;
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000731 std::string rtp_port, rtp_ip, addr_type;
732 GetDefaultDestination(candidates, ICE_CANDIDATE_COMPONENT_RTP,
733 &rtp_port, &rtp_ip, &addr_type);
734 // Found default RTP candidate.
735 // RFC 5245
736 // The default candidates are added to the SDP as the default
737 // destination for media. For streams based on RTP, this is done by
738 // placing the IP address and port of the RTP candidate into the c and m
739 // lines, respectively.
740 // Update the port in the m line.
741 // If this is a m-line with port equal to 0, we don't change it.
742 if (fields[1] != kMediaPortRejected) {
743 new_lines.replace(fields[0].size() + 1,
744 fields[1].size(),
745 rtp_port);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000746 }
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000747 // Add the c line.
748 // RFC 4566
749 // c=<nettype> <addrtype> <connection-address>
750 InitLine(kLineTypeConnection, kConnectionNettype, &os);
751 os << " " << addr_type << " " << rtp_ip;
752 AddLine(os.str(), &new_lines);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000753 message->append(new_lines);
754}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000755
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000756// Gets "a=rtcp" line if found default RTCP candidate from |candidates|.
757static std::string GetRtcpLine(const std::vector<Candidate>& candidates) {
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000758 std::string rtcp_line, rtcp_port, rtcp_ip, addr_type;
759 GetDefaultDestination(candidates, ICE_CANDIDATE_COMPONENT_RTCP,
760 &rtcp_port, &rtcp_ip, &addr_type);
761 // Found default RTCP candidate.
762 // RFC 5245
763 // If the agent is utilizing RTCP, it MUST encode the RTCP candidate
764 // using the a=rtcp attribute as defined in RFC 3605.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000765
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000766 // RFC 3605
767 // rtcp-attribute = "a=rtcp:" port [nettype space addrtype space
768 // connection-address] CRLF
769 std::ostringstream os;
770 InitAttrLine(kAttributeRtcp, &os);
771 os << kSdpDelimiterColon
772 << rtcp_port << " "
773 << kConnectionNettype << " "
774 << addr_type << " "
775 << rtcp_ip;
776 rtcp_line = os.str();
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000777 return rtcp_line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000778}
779
780// Get candidates according to the mline index from SessionDescriptionInterface.
781static void GetCandidatesByMindex(const SessionDescriptionInterface& desci,
782 int mline_index,
783 std::vector<Candidate>* candidates) {
784 if (!candidates) {
785 return;
786 }
787 const IceCandidateCollection* cc = desci.candidates(mline_index);
788 for (size_t i = 0; i < cc->count(); ++i) {
789 const IceCandidateInterface* candidate = cc->at(i);
790 candidates->push_back(candidate->candidate());
791 }
792}
793
deadbeef9d3584c2016-02-16 17:54:10 -0800794std::string SdpSerialize(const JsepSessionDescription& jdesc,
795 bool unified_plan_sdp) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000796 const cricket::SessionDescription* desc = jdesc.description();
797 if (!desc) {
798 return "";
799 }
800
801 std::string message;
802
803 // Session Description.
804 AddLine(kSessionVersion, &message);
805 // Session Origin
806 // RFC 4566
807 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
808 // <unicast-address>
809 std::ostringstream os;
810 InitLine(kLineTypeOrigin, kSessionOriginUsername, &os);
jbauch083b73f2015-07-16 02:46:32 -0700811 const std::string& session_id = jdesc.session_id().empty() ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000812 kSessionOriginSessionId : jdesc.session_id();
jbauch083b73f2015-07-16 02:46:32 -0700813 const std::string& session_version = jdesc.session_version().empty() ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000814 kSessionOriginSessionVersion : jdesc.session_version();
815 os << " " << session_id << " " << session_version << " "
816 << kSessionOriginNettype << " " << kSessionOriginAddrtype << " "
817 << kSessionOriginAddress;
818 AddLine(os.str(), &message);
819 AddLine(kSessionName, &message);
820
821 // Time Description.
822 AddLine(kTimeDescription, &message);
823
824 // Group
825 if (desc->HasGroup(cricket::GROUP_TYPE_BUNDLE)) {
826 std::string group_line = kAttrGroup;
827 const cricket::ContentGroup* group =
828 desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
nisseede5da42017-01-12 05:15:36 -0800829 RTC_DCHECK(group != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000830 const cricket::ContentNames& content_names = group->content_names();
831 for (cricket::ContentNames::const_iterator it = content_names.begin();
832 it != content_names.end(); ++it) {
833 group_line.append(" ");
834 group_line.append(*it);
835 }
836 AddLine(group_line, &message);
837 }
838
839 // MediaStream semantics
840 InitAttrLine(kAttributeMsidSemantics, &os);
841 os << kSdpDelimiterColon << " " << kMediaStreamSemantic;
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000842
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000843 std::set<std::string> media_stream_labels;
844 const ContentInfo* audio_content = GetFirstAudioContent(desc);
845 if (audio_content)
846 GetMediaStreamLabels(audio_content, &media_stream_labels);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000847
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000848 const ContentInfo* video_content = GetFirstVideoContent(desc);
849 if (video_content)
850 GetMediaStreamLabels(video_content, &media_stream_labels);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000851
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000852 for (std::set<std::string>::const_iterator it =
853 media_stream_labels.begin(); it != media_stream_labels.end(); ++it) {
854 os << " " << *it;
855 }
856 AddLine(os.str(), &message);
857
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000858 // Preserve the order of the media contents.
859 int mline_index = -1;
860 for (cricket::ContentInfos::const_iterator it = desc->contents().begin();
861 it != desc->contents().end(); ++it) {
862 const MediaContentDescription* mdesc =
863 static_cast<const MediaContentDescription*>(it->description);
864 std::vector<Candidate> candidates;
865 GetCandidatesByMindex(jdesc, ++mline_index, &candidates);
deadbeef9d3584c2016-02-16 17:54:10 -0800866 BuildMediaDescription(&*it, desc->GetTransportInfoByName(it->name),
867 mdesc->type(), candidates, unified_plan_sdp,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000868 &message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000869 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000870 return message;
871}
872
873// Serializes the passed in IceCandidateInterface to a SDP string.
874// candidate - The candidate to be serialized.
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700875std::string SdpSerializeCandidate(const IceCandidateInterface& candidate) {
876 return SdpSerializeCandidate(candidate.candidate());
877}
878
879// Serializes a cricket Candidate.
880std::string SdpSerializeCandidate(const cricket::Candidate& candidate) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000881 std::string message;
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700882 std::vector<cricket::Candidate> candidates(1, candidate);
honghaiza54a0802015-12-16 18:37:23 -0800883 BuildCandidate(candidates, true, &message);
wu@webrtc.orgec9f5fb2014-06-24 17:05:10 +0000884 // From WebRTC draft section 4.8.1.1 candidate-attribute will be
885 // just candidate:<candidate> not a=candidate:<blah>CRLF
nisseede5da42017-01-12 05:15:36 -0800886 RTC_DCHECK(message.find("a=") == 0);
wu@webrtc.orgec9f5fb2014-06-24 17:05:10 +0000887 message.erase(0, 2);
nisseede5da42017-01-12 05:15:36 -0800888 RTC_DCHECK(message.find(kLineBreak) == message.size() - 2);
wu@webrtc.orgec9f5fb2014-06-24 17:05:10 +0000889 message.resize(message.size() - 2);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000890 return message;
891}
892
893bool SdpDeserialize(const std::string& message,
894 JsepSessionDescription* jdesc,
895 SdpParseError* error) {
896 std::string session_id;
897 std::string session_version;
Peter Thatcher7cbd1882015-09-17 18:54:52 -0700898 TransportDescription session_td("", "");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000899 RtpHeaderExtensions session_extmaps;
900 cricket::SessionDescription* desc = new cricket::SessionDescription();
901 std::vector<JsepIceCandidate*> candidates;
902 size_t current_pos = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000903
904 // Session Description
905 if (!ParseSessionDescription(message, &current_pos, &session_id,
deadbeefc80741f2015-10-22 13:14:45 -0700906 &session_version, &session_td, &session_extmaps,
907 desc, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000908 delete desc;
909 return false;
910 }
911
912 // Media Description
deadbeefc80741f2015-10-22 13:14:45 -0700913 if (!ParseMediaDescription(message, session_td, session_extmaps, &current_pos,
914 desc, &candidates, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000915 delete desc;
916 for (std::vector<JsepIceCandidate*>::const_iterator
917 it = candidates.begin(); it != candidates.end(); ++it) {
918 delete *it;
919 }
920 return false;
921 }
922
923 jdesc->Initialize(desc, session_id, session_version);
924
925 for (std::vector<JsepIceCandidate*>::const_iterator
926 it = candidates.begin(); it != candidates.end(); ++it) {
927 jdesc->AddCandidate(*it);
928 delete *it;
929 }
930 return true;
931}
932
933bool SdpDeserializeCandidate(const std::string& message,
934 JsepIceCandidate* jcandidate,
935 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -0800936 RTC_DCHECK(jcandidate != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000937 Candidate candidate;
938 if (!ParseCandidate(message, &candidate, error, true)) {
939 return false;
940 }
941 jcandidate->SetCandidate(candidate);
942 return true;
943}
944
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700945bool SdpDeserializeCandidate(const std::string& transport_name,
946 const std::string& message,
947 cricket::Candidate* candidate,
948 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -0800949 RTC_DCHECK(candidate != nullptr);
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700950 if (!ParseCandidate(message, candidate, error, true)) {
951 return false;
952 }
953 candidate->set_transport_name(transport_name);
954 return true;
955}
956
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000957bool ParseCandidate(const std::string& message, Candidate* candidate,
958 SdpParseError* error, bool is_raw) {
nisseede5da42017-01-12 05:15:36 -0800959 RTC_DCHECK(candidate != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000960
961 // Get the first line from |message|.
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000962 std::string first_line = message;
963 size_t pos = 0;
964 GetLine(message, &pos, &first_line);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000965
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000966 // Makes sure |message| contains only one line.
967 if (message.size() > first_line.size()) {
968 std::string left, right;
Donald Curtis0e07f922015-05-15 09:21:23 -0700969 if (rtc::tokenize_first(message, kNewLine, &left, &right) &&
970 !right.empty()) {
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000971 return ParseFailed(message, 0, "Expect one line only", error);
972 }
973 }
974
975 // From WebRTC draft section 4.8.1.1 candidate-attribute should be
976 // candidate:<candidate> when trickled, but we still support
977 // a=candidate:<blah>CRLF for backward compatibility and for parsing a line
978 // from the SDP.
979 if (IsLineType(first_line, kLineTypeAttributes)) {
980 first_line = first_line.substr(kLinePrefixLength);
981 }
982
983 std::string attribute_candidate;
984 std::string candidate_value;
985
986 // |first_line| must be in the form of "candidate:<value>".
Donald Curtis144d0182015-05-15 13:14:24 -0700987 if (!rtc::tokenize_first(first_line, kSdpDelimiterColon, &attribute_candidate,
988 &candidate_value) ||
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000989 attribute_candidate != kAttributeCandidate) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000990 if (is_raw) {
991 std::ostringstream description;
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000992 description << "Expect line: " << kAttributeCandidate
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000993 << ":" << "<candidate-str>";
994 return ParseFailed(first_line, 0, description.str(), error);
995 } else {
996 return ParseFailedExpectLine(first_line, 0, kLineTypeAttributes,
997 kAttributeCandidate, error);
998 }
999 }
1000
1001 std::vector<std::string> fields;
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001002 rtc::split(candidate_value, kSdpDelimiterSpace, &fields);
1003
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001004 // RFC 5245
1005 // a=candidate:<foundation> <component-id> <transport> <priority>
1006 // <connection-address> <port> typ <candidate-types>
1007 // [raddr <connection-address>] [rport <port>]
1008 // *(SP extension-att-name SP extension-att-value)
1009 const size_t expected_min_fields = 8;
1010 if (fields.size() < expected_min_fields ||
1011 (fields[6] != kAttributeCandidateTyp)) {
1012 return ParseFailedExpectMinFieldNum(first_line, expected_min_fields, error);
1013 }
jbauch083b73f2015-07-16 02:46:32 -07001014 const std::string& foundation = fields[0];
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001015
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001016 int component_id = 0;
1017 if (!GetValueFromString(first_line, fields[1], &component_id, error)) {
1018 return false;
1019 }
jbauch083b73f2015-07-16 02:46:32 -07001020 const std::string& transport = fields[2];
Peter Boström0c4e06b2015-10-07 12:23:21 +02001021 uint32_t priority = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001022 if (!GetValueFromString(first_line, fields[3], &priority, error)) {
1023 return false;
1024 }
jbauch083b73f2015-07-16 02:46:32 -07001025 const std::string& connection_address = fields[4];
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001026 int port = 0;
1027 if (!GetValueFromString(first_line, fields[5], &port, error)) {
1028 return false;
1029 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001030 SocketAddress address(connection_address, port);
1031
1032 cricket::ProtocolType protocol;
hnslb68cc752016-12-13 10:33:41 -08001033 if (!StringToProto(transport.c_str(), &protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001034 return ParseFailed(first_line, "Unsupported transport type.", error);
1035 }
hnslb68cc752016-12-13 10:33:41 -08001036 switch (protocol) {
1037 case cricket::PROTO_UDP:
1038 case cricket::PROTO_TCP:
1039 case cricket::PROTO_SSLTCP:
1040 // Supported protocol.
1041 break;
1042 default:
1043 return ParseFailed(first_line, "Unsupported transport type.", error);
1044 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001045
1046 std::string candidate_type;
jbauch083b73f2015-07-16 02:46:32 -07001047 const std::string& type = fields[7];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001048 if (type == kCandidateHost) {
1049 candidate_type = cricket::LOCAL_PORT_TYPE;
1050 } else if (type == kCandidateSrflx) {
1051 candidate_type = cricket::STUN_PORT_TYPE;
1052 } else if (type == kCandidateRelay) {
1053 candidate_type = cricket::RELAY_PORT_TYPE;
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001054 } else if (type == kCandidatePrflx) {
1055 candidate_type = cricket::PRFLX_PORT_TYPE;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001056 } else {
1057 return ParseFailed(first_line, "Unsupported candidate type.", error);
1058 }
1059
1060 size_t current_position = expected_min_fields;
1061 SocketAddress related_address;
1062 // The 2 optional fields for related address
1063 // [raddr <connection-address>] [rport <port>]
1064 if (fields.size() >= (current_position + 2) &&
1065 fields[current_position] == kAttributeCandidateRaddr) {
1066 related_address.SetIP(fields[++current_position]);
1067 ++current_position;
1068 }
1069 if (fields.size() >= (current_position + 2) &&
1070 fields[current_position] == kAttributeCandidateRport) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001071 int port = 0;
1072 if (!GetValueFromString(
1073 first_line, fields[++current_position], &port, error)) {
1074 return false;
1075 }
1076 related_address.SetPort(port);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001077 ++current_position;
1078 }
1079
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001080 // If this is a TCP candidate, it has additional extension as defined in
1081 // RFC 6544.
1082 std::string tcptype;
1083 if (fields.size() >= (current_position + 2) &&
1084 fields[current_position] == kTcpCandidateType) {
1085 tcptype = fields[++current_position];
1086 ++current_position;
1087
1088 if (tcptype != cricket::TCPTYPE_ACTIVE_STR &&
1089 tcptype != cricket::TCPTYPE_PASSIVE_STR &&
1090 tcptype != cricket::TCPTYPE_SIMOPEN_STR) {
1091 return ParseFailed(first_line, "Invalid TCP candidate type.", error);
1092 }
1093
1094 if (protocol != cricket::PROTO_TCP) {
1095 return ParseFailed(first_line, "Invalid non-TCP candidate", error);
1096 }
1097 }
1098
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001099 // Extension
honghaiza54a0802015-12-16 18:37:23 -08001100 // Though non-standard, we support the ICE ufrag and pwd being signaled on
1101 // the candidate to avoid issues with confusing which generation a candidate
1102 // belongs to when trickling multiple generations at the same time.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001103 std::string username;
1104 std::string password;
Peter Boström0c4e06b2015-10-07 12:23:21 +02001105 uint32_t generation = 0;
honghaiza0c44ea2016-03-23 16:07:48 -07001106 uint16_t network_id = 0;
1107 uint16_t network_cost = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001108 for (size_t i = current_position; i + 1 < fields.size(); ++i) {
1109 // RFC 5245
1110 // *(SP extension-att-name SP extension-att-value)
1111 if (fields[i] == kAttributeCandidateGeneration) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001112 if (!GetValueFromString(first_line, fields[++i], &generation, error)) {
1113 return false;
1114 }
honghaiza54a0802015-12-16 18:37:23 -08001115 } else if (fields[i] == kAttributeCandidateUfrag) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001116 username = fields[++i];
honghaiza54a0802015-12-16 18:37:23 -08001117 } else if (fields[i] == kAttributeCandidatePwd) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001118 password = fields[++i];
honghaiza0c44ea2016-03-23 16:07:48 -07001119 } else if (fields[i] == kAttributeCandidateNetworkId) {
1120 if (!GetValueFromString(first_line, fields[++i], &network_id, error)) {
1121 return false;
1122 }
honghaize1a0c942016-02-16 14:54:56 -08001123 } else if (fields[i] == kAttributeCandidateNetworkCost) {
1124 if (!GetValueFromString(first_line, fields[++i], &network_cost, error)) {
1125 return false;
1126 }
Honghai Zhang351d77b2016-05-20 15:08:29 -07001127 network_cost = std::min(network_cost, rtc::kNetworkCostMax);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001128 } else {
1129 // Skip the unknown extension.
1130 ++i;
1131 }
1132 }
1133
guoweis@webrtc.org61c12472015-01-15 06:53:07 +00001134 *candidate = Candidate(component_id, cricket::ProtoToString(protocol),
guoweis@webrtc.org950c5182014-12-16 23:01:31 +00001135 address, priority, username, password, candidate_type,
honghaiza0c44ea2016-03-23 16:07:48 -07001136 generation, foundation, network_id, network_cost);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001137 candidate->set_related_address(related_address);
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001138 candidate->set_tcptype(tcptype);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001139 return true;
1140}
1141
1142bool ParseIceOptions(const std::string& line,
1143 std::vector<std::string>* transport_options,
1144 SdpParseError* error) {
1145 std::string ice_options;
1146 if (!GetValue(line, kAttributeIceOption, &ice_options, error)) {
1147 return false;
1148 }
1149 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001150 rtc::split(ice_options, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001151 for (size_t i = 0; i < fields.size(); ++i) {
1152 transport_options->push_back(fields[i]);
1153 }
1154 return true;
1155}
1156
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001157bool ParseSctpPort(const std::string& line,
1158 int* sctp_port,
1159 SdpParseError* error) {
1160 // draft-ietf-mmusic-sctp-sdp-07
1161 // a=sctp-port
1162 std::vector<std::string> fields;
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001163 const size_t expected_min_fields = 2;
lally69f57602015-10-08 10:15:04 -07001164 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColon, &fields);
1165 if (fields.size() < expected_min_fields) {
1166 fields.resize(0);
1167 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpace, &fields);
1168 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001169 if (fields.size() < expected_min_fields) {
1170 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1171 }
1172 if (!rtc::FromString(fields[1], sctp_port)) {
lally69f57602015-10-08 10:15:04 -07001173 return ParseFailed(line, "Invalid sctp port value.", error);
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001174 }
1175 return true;
1176}
1177
isheriff6f8d6862016-05-26 11:24:55 -07001178bool ParseExtmap(const std::string& line,
1179 RtpExtension* extmap,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001180 SdpParseError* error) {
1181 // RFC 5285
1182 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1183 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001184 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001185 kSdpDelimiterSpace, &fields);
1186 const size_t expected_min_fields = 2;
1187 if (fields.size() < expected_min_fields) {
1188 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1189 }
1190 std::string uri = fields[1];
1191
1192 std::string value_direction;
1193 if (!GetValue(fields[0], kAttributeExtmap, &value_direction, error)) {
1194 return false;
1195 }
1196 std::vector<std::string> sub_fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001197 rtc::split(value_direction, kSdpDelimiterSlash, &sub_fields);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001198 int value = 0;
1199 if (!GetValueFromString(line, sub_fields[0], &value, error)) {
1200 return false;
1201 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001202
isheriff6f8d6862016-05-26 11:24:55 -07001203 *extmap = RtpExtension(uri, value);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001204 return true;
1205}
1206
1207void BuildMediaDescription(const ContentInfo* content_info,
1208 const TransportInfo* transport_info,
1209 const MediaType media_type,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001210 const std::vector<Candidate>& candidates,
deadbeef9d3584c2016-02-16 17:54:10 -08001211 bool unified_plan_sdp,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001212 std::string* message) {
nisseede5da42017-01-12 05:15:36 -08001213 RTC_DCHECK(message != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001214 if (content_info == NULL || message == NULL) {
1215 return;
1216 }
1217 // TODO: Rethink if we should use sprintfn instead of stringstream.
1218 // According to the style guide, streams should only be used for logging.
1219 // http://google-styleguide.googlecode.com/svn/
1220 // trunk/cppguide.xml?showone=Streams#Streams
1221 std::ostringstream os;
1222 const MediaContentDescription* media_desc =
henrike@webrtc.org28654cb2013-07-22 21:07:49 +00001223 static_cast<const MediaContentDescription*>(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001224 content_info->description);
nisseede5da42017-01-12 05:15:36 -08001225 RTC_DCHECK(media_desc != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001226
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001227 int sctp_port = cricket::kSctpDefaultPort;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001228
1229 // RFC 4566
1230 // m=<media> <port> <proto> <fmt>
1231 // fmt is a list of payload type numbers that MAY be used in the session.
1232 const char* type = NULL;
1233 if (media_type == cricket::MEDIA_TYPE_AUDIO)
1234 type = kMediaTypeAudio;
1235 else if (media_type == cricket::MEDIA_TYPE_VIDEO)
1236 type = kMediaTypeVideo;
1237 else if (media_type == cricket::MEDIA_TYPE_DATA)
1238 type = kMediaTypeData;
1239 else
nissec80e7412017-01-11 05:56:46 -08001240 RTC_NOTREACHED();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001241
1242 std::string fmt;
1243 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
1244 const VideoContentDescription* video_desc =
1245 static_cast<const VideoContentDescription*>(media_desc);
1246 for (std::vector<cricket::VideoCodec>::const_iterator it =
1247 video_desc->codecs().begin();
1248 it != video_desc->codecs().end(); ++it) {
1249 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001250 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001251 }
1252 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
1253 const AudioContentDescription* audio_desc =
1254 static_cast<const AudioContentDescription*>(media_desc);
1255 for (std::vector<cricket::AudioCodec>::const_iterator it =
1256 audio_desc->codecs().begin();
1257 it != audio_desc->codecs().end(); ++it) {
1258 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001259 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001260 }
1261 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001262 const DataContentDescription* data_desc =
1263 static_cast<const DataContentDescription*>(media_desc);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001264 if (IsDtlsSctp(media_desc->protocol())) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001265 fmt.append(" ");
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001266
1267 for (std::vector<cricket::DataCodec>::const_iterator it =
1268 data_desc->codecs().begin();
1269 it != data_desc->codecs().end(); ++it) {
solenberg9fa49752016-10-08 13:02:44 -07001270 if (cricket::CodecNamesEq(it->name, cricket::kGoogleSctpDataCodecName)
1271 && it->GetParam(cricket::kCodecParamPort, &sctp_port)) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001272 break;
1273 }
1274 }
1275
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001276 fmt.append(rtc::ToString<int>(sctp_port));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001277 } else {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001278 for (std::vector<cricket::DataCodec>::const_iterator it =
1279 data_desc->codecs().begin();
1280 it != data_desc->codecs().end(); ++it) {
1281 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001282 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001283 }
1284 }
1285 }
1286 // The fmt must never be empty. If no codecs are found, set the fmt attribute
1287 // to 0.
1288 if (fmt.empty()) {
1289 fmt = " 0";
1290 }
1291
deadbeef25ed4352016-12-12 18:37:36 -08001292 // The port number in the m line will be updated later when associated with
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001293 // the candidates.
deadbeef25ed4352016-12-12 18:37:36 -08001294 //
1295 // A port value of 0 indicates that the m= section is rejected.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001296 // RFC 3264
1297 // To reject an offered stream, the port number in the corresponding stream in
1298 // the answer MUST be set to zero.
deadbeef25ed4352016-12-12 18:37:36 -08001299 //
1300 // However, the BUNDLE draft adds a new meaning to port zero, when used along
1301 // with a=bundle-only.
1302 const std::string& port =
1303 (content_info->rejected || content_info->bundle_only) ? kMediaPortRejected
1304 : kDummyPort;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001305
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001306 rtc::SSLFingerprint* fp = (transport_info) ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001307 transport_info->description.identity_fingerprint.get() : NULL;
1308
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001309 // Add the m and c lines.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001310 InitLine(kLineTypeMedia, type, &os);
1311 os << " " << port << " " << media_desc->protocol() << fmt;
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001312 std::string mline = os.str();
1313 UpdateMediaDefaultDestination(candidates, mline, message);
1314
1315 // RFC 4566
1316 // b=AS:<bandwidth>
Peter Thatcherc0c3a862015-06-24 15:31:25 -07001317 if (media_desc->bandwidth() >= 1000) {
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001318 InitLine(kLineTypeSessionBandwidth, kApplicationSpecificMaximum, &os);
1319 os << kSdpDelimiterColon << (media_desc->bandwidth() / 1000);
1320 AddLine(os.str(), message);
1321 }
1322
deadbeef25ed4352016-12-12 18:37:36 -08001323 // Add the a=bundle-only line.
1324 if (content_info->bundle_only) {
1325 InitAttrLine(kAttributeBundleOnly, &os);
1326 AddLine(os.str(), message);
1327 }
1328
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001329 // Add the a=rtcp line.
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001330 if (IsRtp(media_desc->protocol())) {
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001331 std::string rtcp_line = GetRtcpLine(candidates);
1332 if (!rtcp_line.empty()) {
1333 AddLine(rtcp_line, message);
1334 }
1335 }
1336
honghaiza54a0802015-12-16 18:37:23 -08001337 // Build the a=candidate lines. We don't include ufrag and pwd in the
1338 // candidates in the SDP to avoid redundancy.
1339 BuildCandidate(candidates, false, message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001340
1341 // Use the transport_info to build the media level ice-ufrag and ice-pwd.
1342 if (transport_info) {
1343 // RFC 5245
1344 // ice-pwd-att = "ice-pwd" ":" password
1345 // ice-ufrag-att = "ice-ufrag" ":" ufrag
1346 // ice-ufrag
deadbeef3f7219b2015-12-28 15:17:14 -08001347 if (!transport_info->description.ice_ufrag.empty()) {
1348 InitAttrLine(kAttributeIceUfrag, &os);
1349 os << kSdpDelimiterColon << transport_info->description.ice_ufrag;
1350 AddLine(os.str(), message);
1351 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001352 // ice-pwd
deadbeef3f7219b2015-12-28 15:17:14 -08001353 if (!transport_info->description.ice_pwd.empty()) {
1354 InitAttrLine(kAttributeIcePwd, &os);
1355 os << kSdpDelimiterColon << transport_info->description.ice_pwd;
1356 AddLine(os.str(), message);
1357 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001358
1359 // draft-petithuguenin-mmusic-ice-attributes-level-03
1360 BuildIceOptions(transport_info->description.transport_options, message);
1361
1362 // RFC 4572
1363 // fingerprint-attribute =
1364 // "fingerprint" ":" hash-func SP fingerprint
1365 if (fp) {
1366 // Insert the fingerprint attribute.
1367 InitAttrLine(kAttributeFingerprint, &os);
1368 os << kSdpDelimiterColon
1369 << fp->algorithm << kSdpDelimiterSpace
1370 << fp->GetRfc4572Fingerprint();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001371 AddLine(os.str(), message);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001372
1373 // Inserting setup attribute.
1374 if (transport_info->description.connection_role !=
1375 cricket::CONNECTIONROLE_NONE) {
1376 // Making sure we are not using "passive" mode.
1377 cricket::ConnectionRole role =
1378 transport_info->description.connection_role;
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001379 std::string dtls_role_str;
1380 VERIFY(cricket::ConnectionRoleToString(role, &dtls_role_str));
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001381 InitAttrLine(kAttributeSetup, &os);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001382 os << kSdpDelimiterColon << dtls_role_str;
1383 AddLine(os.str(), message);
1384 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001385 }
1386 }
1387
1388 // RFC 3388
1389 // mid-attribute = "a=mid:" identification-tag
1390 // identification-tag = token
1391 // Use the content name as the mid identification-tag.
1392 InitAttrLine(kAttributeMid, &os);
1393 os << kSdpDelimiterColon << content_info->name;
1394 AddLine(os.str(), message);
1395
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001396 if (IsDtlsSctp(media_desc->protocol())) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001397 BuildSctpContentAttributes(message, sctp_port);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001398 } else if (IsRtp(media_desc->protocol())) {
deadbeef9d3584c2016-02-16 17:54:10 -08001399 BuildRtpContentAttributes(media_desc, media_type, unified_plan_sdp,
1400 message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001401 }
1402}
1403
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001404void BuildSctpContentAttributes(std::string* message, int sctp_port) {
wu@webrtc.org78187522013-10-07 23:32:02 +00001405 // draft-ietf-mmusic-sctp-sdp-04
1406 // a=sctpmap:sctpmap-number protocol [streams]
lally69f57602015-10-08 10:15:04 -07001407 // TODO(lally): switch this over to mmusic-sctp-sdp-12 (or later), with
1408 // 'a=sctp-port:'
wu@webrtc.org78187522013-10-07 23:32:02 +00001409 std::ostringstream os;
1410 InitAttrLine(kAttributeSctpmap, &os);
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001411 os << kSdpDelimiterColon << sctp_port << kSdpDelimiterSpace
wu@webrtc.org78187522013-10-07 23:32:02 +00001412 << kDefaultSctpmapProtocol << kSdpDelimiterSpace
Taylor Brandstetter1d7a6372016-08-24 13:15:27 -07001413 << cricket::kMaxSctpStreams;
wu@webrtc.org78187522013-10-07 23:32:02 +00001414 AddLine(os.str(), message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001415}
1416
deadbeef9d3584c2016-02-16 17:54:10 -08001417// If unified_plan_sdp is true, will use "a=msid".
1418void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
1419 const MediaType media_type,
1420 bool unified_plan_sdp,
1421 std::string* message) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001422 std::ostringstream os;
1423 // RFC 5285
1424 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1425 // The definitions MUST be either all session level or all media level. This
1426 // implementation uses all media level.
1427 for (size_t i = 0; i < media_desc->rtp_header_extensions().size(); ++i) {
1428 InitAttrLine(kAttributeExtmap, &os);
1429 os << kSdpDelimiterColon << media_desc->rtp_header_extensions()[i].id
1430 << kSdpDelimiterSpace << media_desc->rtp_header_extensions()[i].uri;
1431 AddLine(os.str(), message);
1432 }
1433
1434 // RFC 3264
1435 // a=sendrecv || a=sendonly || a=sendrecv || a=inactive
deadbeefc80741f2015-10-22 13:14:45 -07001436 switch (media_desc->direction()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001437 case cricket::MD_INACTIVE:
1438 InitAttrLine(kAttributeInactive, &os);
1439 break;
1440 case cricket::MD_SENDONLY:
1441 InitAttrLine(kAttributeSendOnly, &os);
1442 break;
1443 case cricket::MD_RECVONLY:
1444 InitAttrLine(kAttributeRecvOnly, &os);
1445 break;
1446 case cricket::MD_SENDRECV:
1447 default:
1448 InitAttrLine(kAttributeSendRecv, &os);
1449 break;
1450 }
1451 AddLine(os.str(), message);
1452
deadbeef9d3584c2016-02-16 17:54:10 -08001453 // draft-ietf-mmusic-msid-11
1454 // a=msid:<stream id> <track id>
1455 if (unified_plan_sdp && !media_desc->streams().empty()) {
1456 if (media_desc->streams().size() > 1u) {
1457 LOG(LS_WARNING) << "Trying to serialize unified plan SDP with more than "
1458 << "one track in a media section. Omitting 'a=msid'.";
1459 } else {
1460 auto track = media_desc->streams().begin();
1461 const std::string& stream_id = track->sync_label;
deadbeef9d3584c2016-02-16 17:54:10 -08001462 InitAttrLine(kAttributeMsid, &os);
1463 os << kSdpDelimiterColon << stream_id << kSdpDelimiterSpace << track->id;
1464 AddLine(os.str(), message);
1465 }
1466 }
1467
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001468 // RFC 5761
1469 // a=rtcp-mux
1470 if (media_desc->rtcp_mux()) {
1471 InitAttrLine(kAttributeRtcpMux, &os);
1472 AddLine(os.str(), message);
1473 }
1474
deadbeef13871492015-12-09 12:37:51 -08001475 // RFC 5506
1476 // a=rtcp-rsize
1477 if (media_desc->rtcp_reduced_size()) {
1478 InitAttrLine(kAttributeRtcpReducedSize, &os);
1479 AddLine(os.str(), message);
1480 }
1481
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001482 // RFC 4568
1483 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
1484 for (std::vector<CryptoParams>::const_iterator it =
1485 media_desc->cryptos().begin();
1486 it != media_desc->cryptos().end(); ++it) {
1487 InitAttrLine(kAttributeCrypto, &os);
1488 os << kSdpDelimiterColon << it->tag << " " << it->cipher_suite << " "
1489 << it->key_params;
1490 if (!it->session_params.empty()) {
1491 os << " " << it->session_params;
1492 }
1493 AddLine(os.str(), message);
1494 }
1495
1496 // RFC 4566
1497 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1498 // [/<encodingparameters>]
1499 BuildRtpMap(media_desc, media_type, message);
1500
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001501 for (StreamParamsVec::const_iterator track = media_desc->streams().begin();
1502 track != media_desc->streams().end(); ++track) {
1503 // Require that the track belongs to a media stream,
1504 // ie the sync_label is set. This extra check is necessary since the
1505 // MediaContentDescription always contains a streamparam with an ssrc even
1506 // if no track or media stream have been created.
1507 if (track->sync_label.empty()) continue;
1508
1509 // Build the ssrc-group lines.
1510 for (size_t i = 0; i < track->ssrc_groups.size(); ++i) {
1511 // RFC 5576
1512 // a=ssrc-group:<semantics> <ssrc-id> ...
1513 if (track->ssrc_groups[i].ssrcs.empty()) {
1514 continue;
1515 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001516 InitAttrLine(kAttributeSsrcGroup, &os);
1517 os << kSdpDelimiterColon << track->ssrc_groups[i].semantics;
Peter Boström0c4e06b2015-10-07 12:23:21 +02001518 std::vector<uint32_t>::const_iterator ssrc =
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001519 track->ssrc_groups[i].ssrcs.begin();
1520 for (; ssrc != track->ssrc_groups[i].ssrcs.end(); ++ssrc) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02001521 os << kSdpDelimiterSpace << rtc::ToString<uint32_t>(*ssrc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001522 }
1523 AddLine(os.str(), message);
1524 }
1525 // Build the ssrc lines for each ssrc.
1526 for (size_t i = 0; i < track->ssrcs.size(); ++i) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02001527 uint32_t ssrc = track->ssrcs[i];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001528 // RFC 5576
1529 // a=ssrc:<ssrc-id> cname:<value>
1530 AddSsrcLine(ssrc, kSsrcAttributeCname,
1531 track->cname, message);
1532
1533 // draft-alvestrand-mmusic-msid-00
1534 // a=ssrc:<ssrc-id> msid:identifier [appdata]
deadbeef9d3584c2016-02-16 17:54:10 -08001535 // The appdata consists of the "id" attribute of a MediaStreamTrack,
1536 // which corresponds to the "id" attribute of StreamParams.
1537 const std::string& stream_id = track->sync_label;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001538 InitAttrLine(kAttributeSsrc, &os);
1539 os << kSdpDelimiterColon << ssrc << kSdpDelimiterSpace
deadbeef9d3584c2016-02-16 17:54:10 -08001540 << kSsrcAttributeMsid << kSdpDelimiterColon << stream_id
1541 << kSdpDelimiterSpace << track->id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001542 AddLine(os.str(), message);
1543
deadbeef9d3584c2016-02-16 17:54:10 -08001544 // TODO(ronghuawu): Remove below code which is for backward
1545 // compatibility.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001546 // draft-alvestrand-rtcweb-mid-01
1547 // a=ssrc:<ssrc-id> mslabel:<value>
1548 // The label isn't yet defined.
1549 // a=ssrc:<ssrc-id> label:<value>
1550 AddSsrcLine(ssrc, kSsrcAttributeMslabel, track->sync_label, message);
1551 AddSsrcLine(ssrc, kSSrcAttributeLabel, track->id, message);
1552 }
1553 }
1554}
1555
1556void WriteFmtpHeader(int payload_type, std::ostringstream* os) {
1557 // fmtp header: a=fmtp:|payload_type| <parameters>
1558 // Add a=fmtp
1559 InitAttrLine(kAttributeFmtp, os);
1560 // Add :|payload_type|
1561 *os << kSdpDelimiterColon << payload_type;
1562}
1563
1564void WriteRtcpFbHeader(int payload_type, std::ostringstream* os) {
1565 // rtcp-fb header: a=rtcp-fb:|payload_type|
1566 // <parameters>/<ccm <ccm_parameters>>
1567 // Add a=rtcp-fb
1568 InitAttrLine(kAttributeRtcpFb, os);
1569 // Add :
1570 *os << kSdpDelimiterColon;
1571 if (payload_type == kWildcardPayloadType) {
1572 *os << "*";
1573 } else {
1574 *os << payload_type;
1575 }
1576}
1577
1578void WriteFmtpParameter(const std::string& parameter_name,
1579 const std::string& parameter_value,
1580 std::ostringstream* os) {
1581 // fmtp parameters: |parameter_name|=|parameter_value|
1582 *os << parameter_name << kSdpDelimiterEqual << parameter_value;
1583}
1584
1585void WriteFmtpParameters(const cricket::CodecParameterMap& parameters,
1586 std::ostringstream* os) {
1587 for (cricket::CodecParameterMap::const_iterator fmtp = parameters.begin();
1588 fmtp != parameters.end(); ++fmtp) {
hta62a216e2016-04-15 11:02:14 -07001589 // Parameters are a semicolon-separated list, no spaces.
1590 // The list is separated from the header by a space.
1591 if (fmtp == parameters.begin()) {
1592 *os << kSdpDelimiterSpace;
1593 } else {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001594 *os << kSdpDelimiterSemicolon;
1595 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001596 WriteFmtpParameter(fmtp->first, fmtp->second, os);
1597 }
1598}
1599
1600bool IsFmtpParam(const std::string& name) {
1601 const char* kFmtpParams[] = {
htaa6b99442016-04-12 10:29:17 -07001602 // TODO(hta): Split FMTP parameters apart from parameters in general.
1603 // FMTP parameters are codec specific, not generic.
1604 kCodecParamMinPTime,
1605 kCodecParamSPropStereo,
1606 kCodecParamStereo,
1607 kCodecParamUseInbandFec,
1608 kCodecParamUseDtx,
1609 kCodecParamStartBitrate,
1610 kCodecParamMaxBitrate,
1611 kCodecParamMinBitrate,
1612 kCodecParamMaxQuantization,
1613 kCodecParamSctpProtocol,
1614 kCodecParamSctpStreams,
1615 kCodecParamMaxAverageBitrate,
1616 kCodecParamMaxPlaybackRate,
1617 kCodecParamAssociatedPayloadType,
1618 cricket::kH264FmtpPacketizationMode,
1619 cricket::kH264FmtpLevelAsymmetryAllowed,
brandtr87d7d772016-11-07 03:03:41 -08001620 cricket::kH264FmtpProfileLevelId,
1621 cricket::kFlexfecFmtpRepairWindow};
tfarina5237aaf2015-11-10 23:44:30 -08001622 for (size_t i = 0; i < arraysize(kFmtpParams); ++i) {
htaa6b99442016-04-12 10:29:17 -07001623 if (name.compare(kFmtpParams[i]) == 0) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001624 return true;
1625 }
1626 }
1627 return false;
1628}
1629
1630// Retreives fmtp parameters from |params|, which may contain other parameters
1631// as well, and puts them in |fmtp_parameters|.
1632void GetFmtpParams(const cricket::CodecParameterMap& params,
1633 cricket::CodecParameterMap* fmtp_parameters) {
1634 for (cricket::CodecParameterMap::const_iterator iter = params.begin();
1635 iter != params.end(); ++iter) {
1636 if (IsFmtpParam(iter->first)) {
1637 (*fmtp_parameters)[iter->first] = iter->second;
1638 }
1639 }
1640}
1641
1642template <class T>
1643void AddFmtpLine(const T& codec, std::string* message) {
1644 cricket::CodecParameterMap fmtp_parameters;
1645 GetFmtpParams(codec.params, &fmtp_parameters);
1646 if (fmtp_parameters.empty()) {
1647 // No need to add an fmtp if it will have no (optional) parameters.
1648 return;
1649 }
1650 std::ostringstream os;
1651 WriteFmtpHeader(codec.id, &os);
1652 WriteFmtpParameters(fmtp_parameters, &os);
1653 AddLine(os.str(), message);
1654 return;
1655}
1656
1657template <class T>
1658void AddRtcpFbLines(const T& codec, std::string* message) {
1659 for (std::vector<cricket::FeedbackParam>::const_iterator iter =
1660 codec.feedback_params.params().begin();
1661 iter != codec.feedback_params.params().end(); ++iter) {
1662 std::ostringstream os;
1663 WriteRtcpFbHeader(codec.id, &os);
1664 os << " " << iter->id();
1665 if (!iter->param().empty()) {
1666 os << " " << iter->param();
1667 }
1668 AddLine(os.str(), message);
1669 }
1670}
1671
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001672bool AddSctpDataCodec(DataContentDescription* media_desc,
1673 int sctp_port) {
solenberg9fa49752016-10-08 13:02:44 -07001674 for (const auto& codec : media_desc->codecs()) {
1675 if (cricket::CodecNamesEq(codec.name, cricket::kGoogleSctpDataCodecName)) {
1676 return ParseFailed("",
1677 "Can't have multiple sctp port attributes.",
1678 NULL);
1679 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001680 }
1681 // Add the SCTP Port number as a pseudo-codec "port" parameter
solenberg9fa49752016-10-08 13:02:44 -07001682 cricket::DataCodec codec_port(cricket::kGoogleSctpDataCodecPlType,
deadbeef67cf2c12016-04-13 10:07:16 -07001683 cricket::kGoogleSctpDataCodecName);
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001684 codec_port.SetParam(cricket::kCodecParamPort, sctp_port);
1685 LOG(INFO) << "AddSctpDataCodec: Got SCTP Port Number "
1686 << sctp_port;
1687 media_desc->AddCodec(codec_port);
1688 return true;
1689}
1690
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001691bool GetMinValue(const std::vector<int>& values, int* value) {
1692 if (values.empty()) {
1693 return false;
1694 }
1695 std::vector<int>::const_iterator found =
1696 std::min_element(values.begin(), values.end());
1697 *value = *found;
1698 return true;
1699}
1700
1701bool GetParameter(const std::string& name,
1702 const cricket::CodecParameterMap& params, int* value) {
1703 std::map<std::string, std::string>::const_iterator found =
1704 params.find(name);
1705 if (found == params.end()) {
1706 return false;
1707 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001708 if (!rtc::FromString(found->second, value)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001709 return false;
1710 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001711 return true;
1712}
1713
1714void BuildRtpMap(const MediaContentDescription* media_desc,
1715 const MediaType media_type,
1716 std::string* message) {
nisseede5da42017-01-12 05:15:36 -08001717 RTC_DCHECK(message != NULL);
1718 RTC_DCHECK(media_desc != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001719 std::ostringstream os;
1720 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
1721 const VideoContentDescription* video_desc =
1722 static_cast<const VideoContentDescription*>(media_desc);
1723 for (std::vector<cricket::VideoCodec>::const_iterator it =
1724 video_desc->codecs().begin();
1725 it != video_desc->codecs().end(); ++it) {
1726 // RFC 4566
1727 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1728 // [/<encodingparameters>]
1729 if (it->id != kWildcardPayloadType) {
1730 InitAttrLine(kAttributeRtpmap, &os);
1731 os << kSdpDelimiterColon << it->id << " " << it->name
1732 << "/" << kDefaultVideoClockrate;
1733 AddLine(os.str(), message);
1734 }
1735 AddRtcpFbLines(*it, message);
1736 AddFmtpLine(*it, message);
1737 }
1738 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
1739 const AudioContentDescription* audio_desc =
1740 static_cast<const AudioContentDescription*>(media_desc);
1741 std::vector<int> ptimes;
1742 std::vector<int> maxptimes;
1743 int max_minptime = 0;
1744 for (std::vector<cricket::AudioCodec>::const_iterator it =
1745 audio_desc->codecs().begin();
1746 it != audio_desc->codecs().end(); ++it) {
nisseede5da42017-01-12 05:15:36 -08001747 RTC_DCHECK(!it->name.empty());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001748 // RFC 4566
1749 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1750 // [/<encodingparameters>]
1751 InitAttrLine(kAttributeRtpmap, &os);
1752 os << kSdpDelimiterColon << it->id << " ";
1753 os << it->name << "/" << it->clockrate;
1754 if (it->channels != 1) {
1755 os << "/" << it->channels;
1756 }
1757 AddLine(os.str(), message);
1758 AddRtcpFbLines(*it, message);
1759 AddFmtpLine(*it, message);
1760 int minptime = 0;
1761 if (GetParameter(kCodecParamMinPTime, it->params, &minptime)) {
1762 max_minptime = std::max(minptime, max_minptime);
1763 }
1764 int ptime;
1765 if (GetParameter(kCodecParamPTime, it->params, &ptime)) {
1766 ptimes.push_back(ptime);
1767 }
1768 int maxptime;
1769 if (GetParameter(kCodecParamMaxPTime, it->params, &maxptime)) {
1770 maxptimes.push_back(maxptime);
1771 }
1772 }
1773 // Populate the maxptime attribute with the smallest maxptime of all codecs
1774 // under the same m-line.
1775 int min_maxptime = INT_MAX;
1776 if (GetMinValue(maxptimes, &min_maxptime)) {
1777 AddAttributeLine(kCodecParamMaxPTime, min_maxptime, message);
1778 }
nisseede5da42017-01-12 05:15:36 -08001779 RTC_DCHECK(min_maxptime > max_minptime);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001780 // Populate the ptime attribute with the smallest ptime or the largest
1781 // minptime, whichever is the largest, for all codecs under the same m-line.
1782 int ptime = INT_MAX;
1783 if (GetMinValue(ptimes, &ptime)) {
1784 ptime = std::min(ptime, min_maxptime);
1785 ptime = std::max(ptime, max_minptime);
1786 AddAttributeLine(kCodecParamPTime, ptime, message);
1787 }
1788 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
1789 const DataContentDescription* data_desc =
1790 static_cast<const DataContentDescription*>(media_desc);
1791 for (std::vector<cricket::DataCodec>::const_iterator it =
1792 data_desc->codecs().begin();
1793 it != data_desc->codecs().end(); ++it) {
1794 // RFC 4566
1795 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1796 // [/<encodingparameters>]
1797 InitAttrLine(kAttributeRtpmap, &os);
1798 os << kSdpDelimiterColon << it->id << " "
1799 << it->name << "/" << it->clockrate;
1800 AddLine(os.str(), message);
1801 }
1802 }
1803}
1804
1805void BuildCandidate(const std::vector<Candidate>& candidates,
honghaiza54a0802015-12-16 18:37:23 -08001806 bool include_ufrag,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001807 std::string* message) {
1808 std::ostringstream os;
1809
1810 for (std::vector<Candidate>::const_iterator it = candidates.begin();
1811 it != candidates.end(); ++it) {
1812 // RFC 5245
1813 // a=candidate:<foundation> <component-id> <transport> <priority>
1814 // <connection-address> <port> typ <candidate-types>
1815 // [raddr <connection-address>] [rport <port>]
1816 // *(SP extension-att-name SP extension-att-value)
1817 std::string type;
1818 // Map the cricket candidate type to "host" / "srflx" / "prflx" / "relay"
1819 if (it->type() == cricket::LOCAL_PORT_TYPE) {
1820 type = kCandidateHost;
1821 } else if (it->type() == cricket::STUN_PORT_TYPE) {
1822 type = kCandidateSrflx;
1823 } else if (it->type() == cricket::RELAY_PORT_TYPE) {
1824 type = kCandidateRelay;
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001825 } else if (it->type() == cricket::PRFLX_PORT_TYPE) {
1826 type = kCandidatePrflx;
1827 // Peer reflexive candidate may be signaled for being removed.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001828 } else {
nissec80e7412017-01-11 05:56:46 -08001829 RTC_NOTREACHED();
Peter Thatcher019087f2015-04-28 09:06:26 -07001830 // Never write out candidates if we don't know the type.
1831 continue;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001832 }
1833
1834 InitAttrLine(kAttributeCandidate, &os);
1835 os << kSdpDelimiterColon
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001836 << it->foundation() << " "
1837 << it->component() << " "
1838 << it->protocol() << " "
1839 << it->priority() << " "
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001840 << it->address().ipaddr().ToString() << " "
1841 << it->address().PortAsString() << " "
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001842 << kAttributeCandidateTyp << " "
1843 << type << " ";
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001844
1845 // Related address
1846 if (!it->related_address().IsNil()) {
1847 os << kAttributeCandidateRaddr << " "
1848 << it->related_address().ipaddr().ToString() << " "
1849 << kAttributeCandidateRport << " "
1850 << it->related_address().PortAsString() << " ";
1851 }
1852
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001853 if (it->protocol() == cricket::TCP_PROTOCOL_NAME) {
mallinath@webrtc.orge999bd02014-08-13 06:05:55 +00001854 os << kTcpCandidateType << " " << it->tcptype() << " ";
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001855 }
1856
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001857 // Extensions
1858 os << kAttributeCandidateGeneration << " " << it->generation();
honghaiza54a0802015-12-16 18:37:23 -08001859 if (include_ufrag && !it->username().empty()) {
1860 os << " " << kAttributeCandidateUfrag << " " << it->username();
1861 }
honghaiza0c44ea2016-03-23 16:07:48 -07001862 if (it->network_id() > 0) {
1863 os << " " << kAttributeCandidateNetworkId << " " << it->network_id();
1864 }
honghaize1a0c942016-02-16 14:54:56 -08001865 if (it->network_cost() > 0) {
1866 os << " " << kAttributeCandidateNetworkCost << " " << it->network_cost();
1867 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001868
1869 AddLine(os.str(), message);
1870 }
1871}
1872
1873void BuildIceOptions(const std::vector<std::string>& transport_options,
1874 std::string* message) {
1875 if (!transport_options.empty()) {
1876 std::ostringstream os;
1877 InitAttrLine(kAttributeIceOption, &os);
1878 os << kSdpDelimiterColon << transport_options[0];
1879 for (size_t i = 1; i < transport_options.size(); ++i) {
1880 os << kSdpDelimiterSpace << transport_options[i];
1881 }
1882 AddLine(os.str(), message);
1883 }
1884}
1885
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001886bool IsRtp(const std::string& protocol) {
1887 return protocol.empty() ||
1888 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
1889}
1890
1891bool IsDtlsSctp(const std::string& protocol) {
1892 // This intentionally excludes "SCTP" and "SCTP/DTLS".
lally@webrtc.orga7470932015-02-24 20:19:21 +00001893 return protocol.find(cricket::kMediaProtocolDtlsSctp) != std::string::npos;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001894}
1895
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001896bool ParseSessionDescription(const std::string& message, size_t* pos,
1897 std::string* session_id,
1898 std::string* session_version,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001899 TransportDescription* session_td,
1900 RtpHeaderExtensions* session_extmaps,
1901 cricket::SessionDescription* desc,
1902 SdpParseError* error) {
1903 std::string line;
1904
deadbeefc80741f2015-10-22 13:14:45 -07001905 desc->set_msid_supported(false);
1906
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001907 // RFC 4566
1908 // v= (protocol version)
1909 if (!GetLineWithType(message, pos, &line, kLineTypeVersion)) {
1910 return ParseFailedExpectLine(message, *pos, kLineTypeVersion,
1911 std::string(), error);
1912 }
1913 // RFC 4566
1914 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
1915 // <unicast-address>
1916 if (!GetLineWithType(message, pos, &line, kLineTypeOrigin)) {
1917 return ParseFailedExpectLine(message, *pos, kLineTypeOrigin,
1918 std::string(), error);
1919 }
1920 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001921 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001922 kSdpDelimiterSpace, &fields);
1923 const size_t expected_fields = 6;
1924 if (fields.size() != expected_fields) {
1925 return ParseFailedExpectFieldNum(line, expected_fields, error);
1926 }
1927 *session_id = fields[1];
1928 *session_version = fields[2];
1929
1930 // RFC 4566
1931 // s= (session name)
1932 if (!GetLineWithType(message, pos, &line, kLineTypeSessionName)) {
1933 return ParseFailedExpectLine(message, *pos, kLineTypeSessionName,
1934 std::string(), error);
1935 }
1936
1937 // Optional lines
1938 // Those are the optional lines, so shouldn't return false if not present.
1939 // RFC 4566
1940 // i=* (session information)
1941 GetLineWithType(message, pos, &line, kLineTypeSessionInfo);
1942
1943 // RFC 4566
1944 // u=* (URI of description)
1945 GetLineWithType(message, pos, &line, kLineTypeSessionUri);
1946
1947 // RFC 4566
1948 // e=* (email address)
1949 GetLineWithType(message, pos, &line, kLineTypeSessionEmail);
1950
1951 // RFC 4566
1952 // p=* (phone number)
1953 GetLineWithType(message, pos, &line, kLineTypeSessionPhone);
1954
1955 // RFC 4566
1956 // c=* (connection information -- not required if included in
1957 // all media)
1958 GetLineWithType(message, pos, &line, kLineTypeConnection);
1959
1960 // RFC 4566
1961 // b=* (zero or more bandwidth information lines)
1962 while (GetLineWithType(message, pos, &line, kLineTypeSessionBandwidth)) {
1963 // By pass zero or more b lines.
1964 }
1965
1966 // RFC 4566
1967 // One or more time descriptions ("t=" and "r=" lines; see below)
1968 // t= (time the session is active)
1969 // r=* (zero or more repeat times)
1970 // Ensure there's at least one time description
1971 if (!GetLineWithType(message, pos, &line, kLineTypeTiming)) {
1972 return ParseFailedExpectLine(message, *pos, kLineTypeTiming, std::string(),
1973 error);
1974 }
1975
1976 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
1977 // By pass zero or more r lines.
1978 }
1979
1980 // Go through the rest of the time descriptions
1981 while (GetLineWithType(message, pos, &line, kLineTypeTiming)) {
1982 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
1983 // By pass zero or more r lines.
1984 }
1985 }
1986
1987 // RFC 4566
1988 // z=* (time zone adjustments)
1989 GetLineWithType(message, pos, &line, kLineTypeTimeZone);
1990
1991 // RFC 4566
1992 // k=* (encryption key)
1993 GetLineWithType(message, pos, &line, kLineTypeEncryptionKey);
1994
1995 // RFC 4566
1996 // a=* (zero or more session attribute lines)
1997 while (GetLineWithType(message, pos, &line, kLineTypeAttributes)) {
1998 if (HasAttribute(line, kAttributeGroup)) {
1999 if (!ParseGroupAttribute(line, desc, error)) {
2000 return false;
2001 }
2002 } else if (HasAttribute(line, kAttributeIceUfrag)) {
2003 if (!GetValue(line, kAttributeIceUfrag,
2004 &(session_td->ice_ufrag), error)) {
2005 return false;
2006 }
2007 } else if (HasAttribute(line, kAttributeIcePwd)) {
2008 if (!GetValue(line, kAttributeIcePwd, &(session_td->ice_pwd), error)) {
2009 return false;
2010 }
2011 } else if (HasAttribute(line, kAttributeIceLite)) {
2012 session_td->ice_mode = cricket::ICEMODE_LITE;
2013 } else if (HasAttribute(line, kAttributeIceOption)) {
2014 if (!ParseIceOptions(line, &(session_td->transport_options), error)) {
2015 return false;
2016 }
2017 } else if (HasAttribute(line, kAttributeFingerprint)) {
2018 if (session_td->identity_fingerprint.get()) {
2019 return ParseFailed(
2020 line,
2021 "Can't have multiple fingerprint attributes at the same level.",
2022 error);
2023 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002024 rtc::SSLFingerprint* fingerprint = NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002025 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
2026 return false;
2027 }
2028 session_td->identity_fingerprint.reset(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002029 } else if (HasAttribute(line, kAttributeSetup)) {
2030 if (!ParseDtlsSetup(line, &(session_td->connection_role), error)) {
2031 return false;
2032 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002033 } else if (HasAttribute(line, kAttributeMsidSemantics)) {
2034 std::string semantics;
2035 if (!GetValue(line, kAttributeMsidSemantics, &semantics, error)) {
2036 return false;
2037 }
deadbeefc80741f2015-10-22 13:14:45 -07002038 desc->set_msid_supported(
2039 CaseInsensitiveFind(semantics, kMediaStreamSemantic));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002040 } else if (HasAttribute(line, kAttributeExtmap)) {
isheriff6f8d6862016-05-26 11:24:55 -07002041 RtpExtension extmap;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002042 if (!ParseExtmap(line, &extmap, error)) {
2043 return false;
2044 }
2045 session_extmaps->push_back(extmap);
2046 }
2047 }
2048
2049 return true;
2050}
2051
2052bool ParseGroupAttribute(const std::string& line,
2053 cricket::SessionDescription* desc,
2054 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08002055 RTC_DCHECK(desc != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002056
2057 // RFC 5888 and draft-holmberg-mmusic-sdp-bundle-negotiation-00
2058 // a=group:BUNDLE video voice
2059 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002060 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002061 kSdpDelimiterSpace, &fields);
2062 std::string semantics;
2063 if (!GetValue(fields[0], kAttributeGroup, &semantics, error)) {
2064 return false;
2065 }
2066 cricket::ContentGroup group(semantics);
2067 for (size_t i = 1; i < fields.size(); ++i) {
2068 group.AddContentName(fields[i]);
2069 }
2070 desc->AddGroup(group);
2071 return true;
2072}
2073
2074static bool ParseFingerprintAttribute(const std::string& line,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002075 rtc::SSLFingerprint** fingerprint,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002076 SdpParseError* error) {
2077 if (!IsLineType(line, kLineTypeAttributes) ||
2078 !HasAttribute(line, kAttributeFingerprint)) {
2079 return ParseFailedExpectLine(line, 0, kLineTypeAttributes,
2080 kAttributeFingerprint, error);
2081 }
2082
2083 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002084 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002085 kSdpDelimiterSpace, &fields);
2086 const size_t expected_fields = 2;
2087 if (fields.size() != expected_fields) {
2088 return ParseFailedExpectFieldNum(line, expected_fields, error);
2089 }
2090
2091 // The first field here is "fingerprint:<hash>.
2092 std::string algorithm;
2093 if (!GetValue(fields[0], kAttributeFingerprint, &algorithm, error)) {
2094 return false;
2095 }
2096
2097 // Downcase the algorithm. Note that we don't need to downcase the
2098 // fingerprint because hex_decode can handle upper-case.
2099 std::transform(algorithm.begin(), algorithm.end(), algorithm.begin(),
2100 ::tolower);
2101
2102 // The second field is the digest value. De-hexify it.
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002103 *fingerprint = rtc::SSLFingerprint::CreateFromRfc4572(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002104 algorithm, fields[1]);
2105 if (!*fingerprint) {
2106 return ParseFailed(line,
2107 "Failed to create fingerprint from the digest.",
2108 error);
2109 }
2110
2111 return true;
2112}
2113
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002114static bool ParseDtlsSetup(const std::string& line,
2115 cricket::ConnectionRole* role,
2116 SdpParseError* error) {
2117 // setup-attr = "a=setup:" role
2118 // role = "active" / "passive" / "actpass" / "holdconn"
2119 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002120 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColon, &fields);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002121 const size_t expected_fields = 2;
2122 if (fields.size() != expected_fields) {
2123 return ParseFailedExpectFieldNum(line, expected_fields, error);
2124 }
2125 std::string role_str = fields[1];
2126 if (!cricket::StringToConnectionRole(role_str, role)) {
2127 return ParseFailed(line, "Invalid attribute value.", error);
2128 }
2129 return true;
2130}
2131
deadbeef9d3584c2016-02-16 17:54:10 -08002132static bool ParseMsidAttribute(const std::string& line,
2133 std::string* stream_id,
2134 std::string* track_id,
2135 SdpParseError* error) {
2136 // draft-ietf-mmusic-msid-11
2137 // a=msid:<stream id> <track id>
2138 // msid-value = msid-id [ SP msid-appdata ]
2139 // msid-id = 1*64token-char ; see RFC 4566
2140 // msid-appdata = 1*64token-char ; see RFC 4566
2141 std::string field1;
2142 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
2143 &field1, track_id)) {
2144 const size_t expected_fields = 2;
2145 return ParseFailedExpectFieldNum(line, expected_fields, error);
2146 }
2147
2148 // msid:<msid-id>
2149 if (!GetValue(field1, kAttributeMsid, stream_id, error)) {
2150 return false;
2151 }
2152 return true;
2153}
2154
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002155// RFC 3551
2156// PT encoding media type clock rate channels
2157// name (Hz)
2158// 0 PCMU A 8,000 1
2159// 1 reserved A
2160// 2 reserved A
2161// 3 GSM A 8,000 1
2162// 4 G723 A 8,000 1
2163// 5 DVI4 A 8,000 1
2164// 6 DVI4 A 16,000 1
2165// 7 LPC A 8,000 1
2166// 8 PCMA A 8,000 1
2167// 9 G722 A 8,000 1
2168// 10 L16 A 44,100 2
2169// 11 L16 A 44,100 1
2170// 12 QCELP A 8,000 1
2171// 13 CN A 8,000 1
2172// 14 MPA A 90,000 (see text)
2173// 15 G728 A 8,000 1
2174// 16 DVI4 A 11,025 1
2175// 17 DVI4 A 22,050 1
2176// 18 G729 A 8,000 1
2177struct StaticPayloadAudioCodec {
2178 const char* name;
2179 int clockrate;
Peter Kasting69558702016-01-12 16:26:35 -08002180 size_t channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002181};
2182static const StaticPayloadAudioCodec kStaticPayloadAudioCodecs[] = {
2183 { "PCMU", 8000, 1 },
2184 { "reserved", 0, 0 },
2185 { "reserved", 0, 0 },
2186 { "GSM", 8000, 1 },
2187 { "G723", 8000, 1 },
2188 { "DVI4", 8000, 1 },
2189 { "DVI4", 16000, 1 },
2190 { "LPC", 8000, 1 },
2191 { "PCMA", 8000, 1 },
2192 { "G722", 8000, 1 },
2193 { "L16", 44100, 2 },
2194 { "L16", 44100, 1 },
2195 { "QCELP", 8000, 1 },
2196 { "CN", 8000, 1 },
2197 { "MPA", 90000, 1 },
2198 { "G728", 8000, 1 },
2199 { "DVI4", 11025, 1 },
2200 { "DVI4", 22050, 1 },
2201 { "G729", 8000, 1 },
2202};
2203
2204void MaybeCreateStaticPayloadAudioCodecs(
2205 const std::vector<int>& fmts, AudioContentDescription* media_desc) {
2206 if (!media_desc) {
2207 return;
2208 }
deadbeef67cf2c12016-04-13 10:07:16 -07002209 RTC_DCHECK(media_desc->codecs().empty());
solenberg9fa49752016-10-08 13:02:44 -07002210 for (int payload_type : fmts) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002211 if (!media_desc->HasCodec(payload_type) &&
2212 payload_type >= 0 &&
kjellander3e33bfe2016-06-20 07:04:09 -07002213 static_cast<uint32_t>(payload_type) <
2214 arraysize(kStaticPayloadAudioCodecs)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002215 std::string encoding_name = kStaticPayloadAudioCodecs[payload_type].name;
2216 int clock_rate = kStaticPayloadAudioCodecs[payload_type].clockrate;
Peter Kasting69558702016-01-12 16:26:35 -08002217 size_t channels = kStaticPayloadAudioCodecs[payload_type].channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002218 media_desc->AddCodec(cricket::AudioCodec(payload_type, encoding_name,
deadbeef67cf2c12016-04-13 10:07:16 -07002219 clock_rate, 0, channels));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002220 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002221 }
2222}
2223
2224template <class C>
2225static C* ParseContentDescription(const std::string& message,
2226 const MediaType media_type,
2227 int mline_index,
2228 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -07002229 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002230 size_t* pos,
2231 std::string* content_name,
deadbeef25ed4352016-12-12 18:37:36 -08002232 bool* bundle_only,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002233 TransportDescription* transport,
2234 std::vector<JsepIceCandidate*>* candidates,
2235 webrtc::SdpParseError* error) {
2236 C* media_desc = new C();
2237 switch (media_type) {
2238 case cricket::MEDIA_TYPE_AUDIO:
2239 *content_name = cricket::CN_AUDIO;
2240 break;
2241 case cricket::MEDIA_TYPE_VIDEO:
2242 *content_name = cricket::CN_VIDEO;
2243 break;
2244 case cricket::MEDIA_TYPE_DATA:
2245 *content_name = cricket::CN_DATA;
2246 break;
2247 default:
nissec80e7412017-01-11 05:56:46 -08002248 RTC_NOTREACHED();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002249 break;
2250 }
deadbeef67cf2c12016-04-13 10:07:16 -07002251 if (!ParseContent(message, media_type, mline_index, protocol, payload_types,
deadbeef25ed4352016-12-12 18:37:36 -08002252 pos, content_name, bundle_only, media_desc, transport,
2253 candidates, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002254 delete media_desc;
2255 return NULL;
2256 }
2257 // Sort the codecs according to the m-line fmt list.
deadbeef67cf2c12016-04-13 10:07:16 -07002258 std::unordered_map<int, int> payload_type_preferences;
2259 // "size + 1" so that the lowest preference payload type has a preference of
2260 // 1, which is greater than the default (0) for payload types not in the fmt
2261 // list.
2262 int preference = static_cast<int>(payload_types.size() + 1);
2263 for (int pt : payload_types) {
2264 payload_type_preferences[pt] = preference--;
2265 }
2266 std::vector<typename C::CodecType> codecs = media_desc->codecs();
2267 std::sort(codecs.begin(), codecs.end(), [&payload_type_preferences](
2268 const typename C::CodecType& a,
2269 const typename C::CodecType& b) {
2270 return payload_type_preferences[a.id] > payload_type_preferences[b.id];
2271 });
2272 media_desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002273 return media_desc;
2274}
2275
2276bool ParseMediaDescription(const std::string& message,
2277 const TransportDescription& session_td,
2278 const RtpHeaderExtensions& session_extmaps,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002279 size_t* pos,
2280 cricket::SessionDescription* desc,
2281 std::vector<JsepIceCandidate*>* candidates,
2282 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08002283 RTC_DCHECK(desc != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002284 std::string line;
2285 int mline_index = -1;
2286
2287 // Zero or more media descriptions
2288 // RFC 4566
2289 // m=<media> <port> <proto> <fmt>
2290 while (GetLineWithType(message, pos, &line, kLineTypeMedia)) {
2291 ++mline_index;
2292
2293 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002294 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002295 kSdpDelimiterSpace, &fields);
2296 const size_t expected_min_fields = 4;
2297 if (fields.size() < expected_min_fields) {
2298 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2299 }
deadbeef25ed4352016-12-12 18:37:36 -08002300 bool port_rejected = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002301 // RFC 3264
2302 // To reject an offered stream, the port number in the corresponding stream
2303 // in the answer MUST be set to zero.
2304 if (fields[1] == kMediaPortRejected) {
deadbeef25ed4352016-12-12 18:37:36 -08002305 port_rejected = true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002306 }
2307
2308 std::string protocol = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002309
2310 // <fmt>
deadbeef67cf2c12016-04-13 10:07:16 -07002311 std::vector<int> payload_types;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002312 if (IsRtp(protocol)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002313 for (size_t j = 3 ; j < fields.size(); ++j) {
2314 // TODO(wu): Remove when below bug is fixed.
2315 // https://bugzilla.mozilla.org/show_bug.cgi?id=996329
pbosbb36fdf2015-07-09 07:48:14 -07002316 if (fields[j].empty() && j == fields.size() - 1) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002317 continue;
2318 }
wu@webrtc.org36eda7c2014-04-15 20:37:30 +00002319
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002320 int pl = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00002321 if (!GetPayloadTypeFromString(line, fields[j], &pl, error)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002322 return false;
2323 }
deadbeef67cf2c12016-04-13 10:07:16 -07002324 payload_types.push_back(pl);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002325 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002326 }
2327
2328 // Make a temporary TransportDescription based on |session_td|.
2329 // Some of this gets overwritten by ParseContent.
deadbeef46eed762016-01-28 13:24:37 -08002330 TransportDescription transport(
2331 session_td.transport_options, session_td.ice_ufrag, session_td.ice_pwd,
2332 session_td.ice_mode, session_td.connection_role,
2333 session_td.identity_fingerprint.get());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002334
kwibergd1fe2812016-04-27 06:47:29 -07002335 std::unique_ptr<MediaContentDescription> content;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002336 std::string content_name;
deadbeef25ed4352016-12-12 18:37:36 -08002337 bool bundle_only = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002338 if (HasAttribute(line, kMediaTypeVideo)) {
2339 content.reset(ParseContentDescription<VideoContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002340 message, cricket::MEDIA_TYPE_VIDEO, mline_index, protocol,
deadbeef25ed4352016-12-12 18:37:36 -08002341 payload_types, pos, &content_name, &bundle_only, &transport,
2342 candidates, error));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002343 } else if (HasAttribute(line, kMediaTypeAudio)) {
2344 content.reset(ParseContentDescription<AudioContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002345 message, cricket::MEDIA_TYPE_AUDIO, mline_index, protocol,
deadbeef25ed4352016-12-12 18:37:36 -08002346 payload_types, pos, &content_name, &bundle_only, &transport,
2347 candidates, error));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002348 } else if (HasAttribute(line, kMediaTypeData)) {
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002349 DataContentDescription* data_desc =
wu@webrtc.org78187522013-10-07 23:32:02 +00002350 ParseContentDescription<DataContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002351 message, cricket::MEDIA_TYPE_DATA, mline_index, protocol,
deadbeef25ed4352016-12-12 18:37:36 -08002352 payload_types, pos, &content_name, &bundle_only, &transport,
2353 candidates, error);
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002354 content.reset(data_desc);
wu@webrtc.org78187522013-10-07 23:32:02 +00002355
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002356 int p;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002357 if (data_desc && IsDtlsSctp(protocol) && rtc::FromString(fields[3], &p)) {
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002358 if (!AddSctpDataCodec(data_desc, p))
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002359 return false;
wu@webrtc.org78187522013-10-07 23:32:02 +00002360 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002361 } else {
2362 LOG(LS_WARNING) << "Unsupported media type: " << line;
2363 continue;
2364 }
2365 if (!content.get()) {
2366 // ParseContentDescription returns NULL if failed.
2367 return false;
2368 }
2369
deadbeef25ed4352016-12-12 18:37:36 -08002370 bool content_rejected = false;
deadbeef12771a12017-01-03 13:53:47 -08002371 // A port of 0 is not interpreted as a rejected m= section when it's
2372 // used along with a=bundle-only.
deadbeef25ed4352016-12-12 18:37:36 -08002373 if (bundle_only) {
deadbeef25ed4352016-12-12 18:37:36 -08002374 if (!port_rejected) {
deadbeef12771a12017-01-03 13:53:47 -08002375 // Usage of bundle-only with a nonzero port is unspecified. So just
2376 // ignore bundle-only if we see this.
2377 bundle_only = false;
2378 LOG(LS_WARNING)
2379 << "a=bundle-only attribute observed with a nonzero "
2380 << "port; this usage is unspecified so the attribute is being "
2381 << "ignored.";
deadbeef25ed4352016-12-12 18:37:36 -08002382 }
2383 } else {
2384 // If not using bundle-only, interpret port 0 in the normal way; the m=
2385 // section is being rejected.
2386 content_rejected = port_rejected;
2387 }
2388
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002389 if (IsRtp(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002390 // Set the extmap.
2391 if (!session_extmaps.empty() &&
2392 !content->rtp_header_extensions().empty()) {
2393 return ParseFailed("",
2394 "The a=extmap MUST be either all session level or "
2395 "all media level.",
2396 error);
2397 }
2398 for (size_t i = 0; i < session_extmaps.size(); ++i) {
2399 content->AddRtpHeaderExtension(session_extmaps[i]);
2400 }
2401 }
2402 content->set_protocol(protocol);
2403 desc->AddContent(content_name,
deadbeef25ed4352016-12-12 18:37:36 -08002404 IsDtlsSctp(protocol) ? cricket::NS_JINGLE_DRAFT_SCTP
2405 : cricket::NS_JINGLE_RTP,
2406 content_rejected, bundle_only, content.release());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002407 // Create TransportInfo with the media level "ice-pwd" and "ice-ufrag".
2408 TransportInfo transport_info(content_name, transport);
2409
2410 if (!desc->AddTransportInfo(transport_info)) {
2411 std::ostringstream description;
2412 description << "Failed to AddTransportInfo with content name: "
2413 << content_name;
2414 return ParseFailed("", description.str(), error);
2415 }
2416 }
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00002417
2418 size_t end_of_message = message.size();
2419 if (mline_index == -1 && *pos != end_of_message) {
2420 ParseFailed(message, *pos, "Expects m line.", error);
2421 return false;
2422 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002423 return true;
2424}
2425
2426bool VerifyCodec(const cricket::Codec& codec) {
2427 // Codec has not been populated correctly unless the name has been set. This
2428 // can happen if an SDP has an fmtp or rtcp-fb with a payload type but doesn't
2429 // have a corresponding "rtpmap" line.
htab39db842016-12-08 01:50:48 -08002430 return !codec.name.empty();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002431}
2432
2433bool VerifyAudioCodecs(const AudioContentDescription* audio_desc) {
2434 const std::vector<cricket::AudioCodec>& codecs = audio_desc->codecs();
2435 for (std::vector<cricket::AudioCodec>::const_iterator iter = codecs.begin();
2436 iter != codecs.end(); ++iter) {
2437 if (!VerifyCodec(*iter)) {
2438 return false;
2439 }
2440 }
2441 return true;
2442}
2443
2444bool VerifyVideoCodecs(const VideoContentDescription* video_desc) {
2445 const std::vector<cricket::VideoCodec>& codecs = video_desc->codecs();
2446 for (std::vector<cricket::VideoCodec>::const_iterator iter = codecs.begin();
2447 iter != codecs.end(); ++iter) {
2448 if (!VerifyCodec(*iter)) {
2449 return false;
2450 }
2451 }
2452 return true;
2453}
2454
2455void AddParameters(const cricket::CodecParameterMap& parameters,
2456 cricket::Codec* codec) {
2457 for (cricket::CodecParameterMap::const_iterator iter =
2458 parameters.begin(); iter != parameters.end(); ++iter) {
2459 codec->SetParam(iter->first, iter->second);
2460 }
2461}
2462
2463void AddFeedbackParameter(const cricket::FeedbackParam& feedback_param,
2464 cricket::Codec* codec) {
2465 codec->AddFeedbackParam(feedback_param);
2466}
2467
2468void AddFeedbackParameters(const cricket::FeedbackParams& feedback_params,
2469 cricket::Codec* codec) {
2470 for (std::vector<cricket::FeedbackParam>::const_iterator iter =
2471 feedback_params.params().begin();
2472 iter != feedback_params.params().end(); ++iter) {
2473 codec->AddFeedbackParam(*iter);
2474 }
2475}
2476
2477// Gets the current codec setting associated with |payload_type|. If there
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002478// is no Codec associated with that payload type it returns an empty codec
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002479// with that payload type.
2480template <class T>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002481T GetCodecWithPayloadType(const std::vector<T>& codecs, int payload_type) {
magjedb05fa242016-11-11 04:00:16 -08002482 const T* codec = FindCodecById(codecs, payload_type);
2483 if (codec)
2484 return *codec;
2485 // Return empty codec with |payload_type|.
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002486 T ret_val;
magjedb05fa242016-11-11 04:00:16 -08002487 ret_val.id = payload_type;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002488 return ret_val;
2489}
2490
2491// Updates or creates a new codec entry in the audio description.
2492template <class T, class U>
2493void AddOrReplaceCodec(MediaContentDescription* content_desc, const U& codec) {
2494 T* desc = static_cast<T*>(content_desc);
2495 std::vector<U> codecs = desc->codecs();
2496 bool found = false;
2497
2498 typename std::vector<U>::iterator iter;
2499 for (iter = codecs.begin(); iter != codecs.end(); ++iter) {
2500 if (iter->id == codec.id) {
2501 *iter = codec;
2502 found = true;
2503 break;
2504 }
2505 }
2506 if (!found) {
2507 desc->AddCodec(codec);
2508 return;
2509 }
2510 desc->set_codecs(codecs);
2511}
2512
2513// Adds or updates existing codec corresponding to |payload_type| according
2514// to |parameters|.
2515template <class T, class U>
2516void UpdateCodec(MediaContentDescription* content_desc, int payload_type,
2517 const cricket::CodecParameterMap& parameters) {
2518 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002519 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2520 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002521 AddParameters(parameters, &new_codec);
2522 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2523}
2524
2525// Adds or updates existing codec corresponding to |payload_type| according
2526// to |feedback_param|.
2527template <class T, class U>
2528void UpdateCodec(MediaContentDescription* content_desc, int payload_type,
2529 const cricket::FeedbackParam& feedback_param) {
2530 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002531 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2532 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002533 AddFeedbackParameter(feedback_param, &new_codec);
2534 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2535}
2536
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002537template <class T>
2538bool PopWildcardCodec(std::vector<T>* codecs, T* wildcard_codec) {
2539 for (auto iter = codecs->begin(); iter != codecs->end(); ++iter) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002540 if (iter->id == kWildcardPayloadType) {
2541 *wildcard_codec = *iter;
2542 codecs->erase(iter);
2543 return true;
2544 }
2545 }
2546 return false;
2547}
2548
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002549template<class T>
2550void UpdateFromWildcardCodecs(cricket::MediaContentDescriptionImpl<T>* desc) {
2551 auto codecs = desc->codecs();
2552 T wildcard_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002553 if (!PopWildcardCodec(&codecs, &wildcard_codec)) {
2554 return;
2555 }
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002556 for (auto& codec : codecs) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002557 AddFeedbackParameters(wildcard_codec.feedback_params, &codec);
2558 }
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002559 desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002560}
2561
2562void AddAudioAttribute(const std::string& name, const std::string& value,
2563 AudioContentDescription* audio_desc) {
2564 if (value.empty()) {
2565 return;
2566 }
2567 std::vector<cricket::AudioCodec> codecs = audio_desc->codecs();
2568 for (std::vector<cricket::AudioCodec>::iterator iter = codecs.begin();
2569 iter != codecs.end(); ++iter) {
2570 iter->params[name] = value;
2571 }
2572 audio_desc->set_codecs(codecs);
2573}
2574
2575bool ParseContent(const std::string& message,
2576 const MediaType media_type,
2577 int mline_index,
2578 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -07002579 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002580 size_t* pos,
2581 std::string* content_name,
deadbeef25ed4352016-12-12 18:37:36 -08002582 bool* bundle_only,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002583 MediaContentDescription* media_desc,
2584 TransportDescription* transport,
2585 std::vector<JsepIceCandidate*>* candidates,
2586 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08002587 RTC_DCHECK(media_desc != NULL);
2588 RTC_DCHECK(content_name != NULL);
2589 RTC_DCHECK(transport != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002590
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +00002591 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
2592 MaybeCreateStaticPayloadAudioCodecs(
deadbeef67cf2c12016-04-13 10:07:16 -07002593 payload_types, static_cast<AudioContentDescription*>(media_desc));
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +00002594 }
2595
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002596 // The media level "ice-ufrag" and "ice-pwd".
2597 // The candidates before update the media level "ice-pwd" and "ice-ufrag".
2598 Candidates candidates_orig;
2599 std::string line;
2600 std::string mline_id;
2601 // Tracks created out of the ssrc attributes.
2602 StreamParamsVec tracks;
2603 SsrcInfoVec ssrc_infos;
2604 SsrcGroupVec ssrc_groups;
2605 std::string maxptime_as_string;
2606 std::string ptime_as_string;
deadbeef9d3584c2016-02-16 17:54:10 -08002607 std::string stream_id;
2608 std::string track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002609
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002610 // Loop until the next m line
2611 while (!IsLineType(message, kLineTypeMedia, *pos)) {
2612 if (!GetLine(message, pos, &line)) {
2613 if (*pos >= message.size()) {
2614 break; // Done parsing
2615 } else {
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +00002616 return ParseFailed(message, *pos, "Invalid SDP line.", error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002617 }
2618 }
2619
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002620 // RFC 4566
2621 // b=* (zero or more bandwidth information lines)
2622 if (IsLineType(line, kLineTypeSessionBandwidth)) {
2623 std::string bandwidth;
2624 if (HasAttribute(line, kApplicationSpecificMaximum)) {
2625 if (!GetValue(line, kApplicationSpecificMaximum, &bandwidth, error)) {
2626 return false;
2627 } else {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002628 int b = 0;
2629 if (!GetValueFromString(line, bandwidth, &b, error)) {
2630 return false;
2631 }
Peter Thatcherc0c3a862015-06-24 15:31:25 -07002632 // We should never use more than the default bandwidth for RTP-based
2633 // data channels. Don't allow SDP to set the bandwidth, because
2634 // that would give JS the opportunity to "break the Internet".
2635 // See: https://code.google.com/p/chromium/issues/detail?id=280726
2636 if (media_type == cricket::MEDIA_TYPE_DATA && IsRtp(protocol) &&
2637 b > cricket::kDataMaxBandwidth / 1000) {
2638 std::ostringstream description;
2639 description << "RTP-based data channels may not send more than "
2640 << cricket::kDataMaxBandwidth / 1000 << "kbps.";
2641 return ParseFailed(line, description.str(), error);
2642 }
deadbeefb2362572016-12-13 16:37:06 -08002643 // Prevent integer overflow.
2644 b = std::min(b, INT_MAX / 1000);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002645 media_desc->set_bandwidth(b * 1000);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002646 }
2647 }
2648 continue;
2649 }
2650
2651 if (!IsLineType(line, kLineTypeAttributes)) {
2652 // TODO: Handle other lines if needed.
2653 LOG(LS_INFO) << "Ignored line: " << line;
2654 continue;
2655 }
2656
2657 // Handle attributes common to SCTP and RTP.
2658 if (HasAttribute(line, kAttributeMid)) {
2659 // RFC 3388
2660 // mid-attribute = "a=mid:" identification-tag
2661 // identification-tag = token
2662 // Use the mid identification-tag as the content name.
2663 if (!GetValue(line, kAttributeMid, &mline_id, error)) {
2664 return false;
2665 }
2666 *content_name = mline_id;
deadbeef25ed4352016-12-12 18:37:36 -08002667 } else if (HasAttribute(line, kAttributeBundleOnly)) {
2668 *bundle_only = true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002669 } else if (HasAttribute(line, kAttributeCandidate)) {
2670 Candidate candidate;
2671 if (!ParseCandidate(line, &candidate, error, false)) {
2672 return false;
2673 }
deadbeef7bcdb692017-01-20 12:43:58 -08002674 // ParseCandidate will parse non-standard ufrag and password attributes,
2675 // since it's used for candidate trickling, but we only want to process
2676 // the "a=ice-ufrag"/"a=ice-pwd" values in a session description, so
2677 // strip them off at this point.
2678 candidate.set_username(std::string());
2679 candidate.set_password(std::string());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002680 candidates_orig.push_back(candidate);
2681 } else if (HasAttribute(line, kAttributeIceUfrag)) {
2682 if (!GetValue(line, kAttributeIceUfrag, &transport->ice_ufrag, error)) {
2683 return false;
2684 }
2685 } else if (HasAttribute(line, kAttributeIcePwd)) {
2686 if (!GetValue(line, kAttributeIcePwd, &transport->ice_pwd, error)) {
2687 return false;
2688 }
2689 } else if (HasAttribute(line, kAttributeIceOption)) {
2690 if (!ParseIceOptions(line, &transport->transport_options, error)) {
2691 return false;
2692 }
2693 } else if (HasAttribute(line, kAttributeFmtp)) {
2694 if (!ParseFmtpAttributes(line, media_type, media_desc, error)) {
2695 return false;
2696 }
2697 } else if (HasAttribute(line, kAttributeFingerprint)) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002698 rtc::SSLFingerprint* fingerprint = NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002699
2700 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
2701 return false;
2702 }
2703 transport->identity_fingerprint.reset(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002704 } else if (HasAttribute(line, kAttributeSetup)) {
2705 if (!ParseDtlsSetup(line, &(transport->connection_role), error)) {
2706 return false;
2707 }
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002708 } else if (IsDtlsSctp(protocol) && HasAttribute(line, kAttributeSctpPort)) {
deadbeef7e146cb2016-09-28 10:04:34 -07002709 if (media_type != cricket::MEDIA_TYPE_DATA) {
2710 return ParseFailed(
2711 line, "sctp-port attribute found in non-data media description.",
2712 error);
2713 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002714 int sctp_port;
2715 if (!ParseSctpPort(line, &sctp_port, error)) {
2716 return false;
2717 }
2718 if (!AddSctpDataCodec(static_cast<DataContentDescription*>(media_desc),
2719 sctp_port)) {
2720 return false;
2721 }
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002722 } else if (IsRtp(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002723 //
2724 // RTP specific attrubtes
2725 //
2726 if (HasAttribute(line, kAttributeRtcpMux)) {
2727 media_desc->set_rtcp_mux(true);
deadbeef13871492015-12-09 12:37:51 -08002728 } else if (HasAttribute(line, kAttributeRtcpReducedSize)) {
2729 media_desc->set_rtcp_reduced_size(true);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002730 } else if (HasAttribute(line, kAttributeSsrcGroup)) {
2731 if (!ParseSsrcGroupAttribute(line, &ssrc_groups, error)) {
2732 return false;
2733 }
2734 } else if (HasAttribute(line, kAttributeSsrc)) {
2735 if (!ParseSsrcAttribute(line, &ssrc_infos, error)) {
2736 return false;
2737 }
2738 } else if (HasAttribute(line, kAttributeCrypto)) {
2739 if (!ParseCryptoAttribute(line, media_desc, error)) {
2740 return false;
2741 }
2742 } else if (HasAttribute(line, kAttributeRtpmap)) {
deadbeef67cf2c12016-04-13 10:07:16 -07002743 if (!ParseRtpmapAttribute(line, media_type, payload_types, media_desc,
2744 error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002745 return false;
2746 }
2747 } else if (HasAttribute(line, kCodecParamMaxPTime)) {
2748 if (!GetValue(line, kCodecParamMaxPTime, &maxptime_as_string, error)) {
2749 return false;
2750 }
2751 } else if (HasAttribute(line, kAttributeRtcpFb)) {
2752 if (!ParseRtcpFbAttribute(line, media_type, media_desc, error)) {
2753 return false;
2754 }
2755 } else if (HasAttribute(line, kCodecParamPTime)) {
2756 if (!GetValue(line, kCodecParamPTime, &ptime_as_string, error)) {
2757 return false;
2758 }
2759 } else if (HasAttribute(line, kAttributeSendOnly)) {
2760 media_desc->set_direction(cricket::MD_SENDONLY);
2761 } else if (HasAttribute(line, kAttributeRecvOnly)) {
2762 media_desc->set_direction(cricket::MD_RECVONLY);
2763 } else if (HasAttribute(line, kAttributeInactive)) {
2764 media_desc->set_direction(cricket::MD_INACTIVE);
2765 } else if (HasAttribute(line, kAttributeSendRecv)) {
2766 media_desc->set_direction(cricket::MD_SENDRECV);
2767 } else if (HasAttribute(line, kAttributeExtmap)) {
isheriff6f8d6862016-05-26 11:24:55 -07002768 RtpExtension extmap;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002769 if (!ParseExtmap(line, &extmap, error)) {
2770 return false;
2771 }
2772 media_desc->AddRtpHeaderExtension(extmap);
2773 } else if (HasAttribute(line, kAttributeXGoogleFlag)) {
2774 // Experimental attribute. Conference mode activates more aggressive
2775 // AEC and NS settings.
2776 // TODO: expose API to set these directly.
2777 std::string flag_value;
2778 if (!GetValue(line, kAttributeXGoogleFlag, &flag_value, error)) {
2779 return false;
2780 }
2781 if (flag_value.compare(kValueConference) == 0)
2782 media_desc->set_conference_mode(true);
deadbeef9d3584c2016-02-16 17:54:10 -08002783 } else if (HasAttribute(line, kAttributeMsid)) {
2784 if (!ParseMsidAttribute(line, &stream_id, &track_id, error)) {
2785 return false;
2786 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002787 }
2788 } else {
2789 // Only parse lines that we are interested of.
2790 LOG(LS_INFO) << "Ignored line: " << line;
2791 continue;
2792 }
2793 }
2794
2795 // Create tracks from the |ssrc_infos|.
Taylor Brandstetter5de6b752016-03-09 17:02:30 -08002796 // If the stream_id/track_id for all SSRCS are identical, one StreamParams
2797 // will be created in CreateTracksFromSsrcInfos, containing all the SSRCs from
2798 // the m= section.
2799 CreateTracksFromSsrcInfos(ssrc_infos, stream_id, track_id, &tracks);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002800
2801 // Add the ssrc group to the track.
2802 for (SsrcGroupVec::iterator ssrc_group = ssrc_groups.begin();
2803 ssrc_group != ssrc_groups.end(); ++ssrc_group) {
2804 if (ssrc_group->ssrcs.empty()) {
2805 continue;
2806 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002807 uint32_t ssrc = ssrc_group->ssrcs.front();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002808 for (StreamParamsVec::iterator track = tracks.begin();
2809 track != tracks.end(); ++track) {
2810 if (track->has_ssrc(ssrc)) {
2811 track->ssrc_groups.push_back(*ssrc_group);
2812 }
2813 }
2814 }
2815
2816 // Add the new tracks to the |media_desc|.
deadbeef9d3584c2016-02-16 17:54:10 -08002817 for (StreamParams& track : tracks) {
2818 media_desc->AddStream(track);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002819 }
2820
2821 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
2822 AudioContentDescription* audio_desc =
2823 static_cast<AudioContentDescription*>(media_desc);
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002824 UpdateFromWildcardCodecs(audio_desc);
2825
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002826 // Verify audio codec ensures that no audio codec has been populated with
2827 // only fmtp.
2828 if (!VerifyAudioCodecs(audio_desc)) {
2829 return ParseFailed("Failed to parse audio codecs correctly.", error);
2830 }
2831 AddAudioAttribute(kCodecParamMaxPTime, maxptime_as_string, audio_desc);
2832 AddAudioAttribute(kCodecParamPTime, ptime_as_string, audio_desc);
2833 }
2834
2835 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002836 VideoContentDescription* video_desc =
2837 static_cast<VideoContentDescription*>(media_desc);
2838 UpdateFromWildcardCodecs(video_desc);
2839 // Verify video codec ensures that no video codec has been populated with
2840 // only rtcp-fb.
2841 if (!VerifyVideoCodecs(video_desc)) {
2842 return ParseFailed("Failed to parse video codecs correctly.", error);
2843 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002844 }
2845
2846 // RFC 5245
2847 // Update the candidates with the media level "ice-pwd" and "ice-ufrag".
2848 for (Candidates::iterator it = candidates_orig.begin();
2849 it != candidates_orig.end(); ++it) {
nisseede5da42017-01-12 05:15:36 -08002850 RTC_DCHECK((*it).username().empty() ||
2851 (*it).username() == transport->ice_ufrag);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002852 (*it).set_username(transport->ice_ufrag);
nisseede5da42017-01-12 05:15:36 -08002853 RTC_DCHECK((*it).password().empty());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002854 (*it).set_password(transport->ice_pwd);
2855 candidates->push_back(
2856 new JsepIceCandidate(mline_id, mline_index, *it));
2857 }
2858 return true;
2859}
2860
2861bool ParseSsrcAttribute(const std::string& line, SsrcInfoVec* ssrc_infos,
2862 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08002863 RTC_DCHECK(ssrc_infos != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002864 // RFC 5576
2865 // a=ssrc:<ssrc-id> <attribute>
2866 // a=ssrc:<ssrc-id> <attribute>:<value>
2867 std::string field1, field2;
Donald Curtis144d0182015-05-15 13:14:24 -07002868 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
2869 &field1, &field2)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002870 const size_t expected_fields = 2;
2871 return ParseFailedExpectFieldNum(line, expected_fields, error);
2872 }
2873
2874 // ssrc:<ssrc-id>
2875 std::string ssrc_id_s;
2876 if (!GetValue(field1, kAttributeSsrc, &ssrc_id_s, error)) {
2877 return false;
2878 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002879 uint32_t ssrc_id = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002880 if (!GetValueFromString(line, ssrc_id_s, &ssrc_id, error)) {
2881 return false;
2882 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002883
2884 std::string attribute;
2885 std::string value;
Donald Curtis144d0182015-05-15 13:14:24 -07002886 if (!rtc::tokenize_first(field2, kSdpDelimiterColon, &attribute, &value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002887 std::ostringstream description;
2888 description << "Failed to get the ssrc attribute value from " << field2
2889 << ". Expected format <attribute>:<value>.";
2890 return ParseFailed(line, description.str(), error);
2891 }
2892
2893 // Check if there's already an item for this |ssrc_id|. Create a new one if
2894 // there isn't.
2895 SsrcInfoVec::iterator ssrc_info = ssrc_infos->begin();
2896 for (; ssrc_info != ssrc_infos->end(); ++ssrc_info) {
2897 if (ssrc_info->ssrc_id == ssrc_id) {
2898 break;
2899 }
2900 }
2901 if (ssrc_info == ssrc_infos->end()) {
2902 SsrcInfo info;
2903 info.ssrc_id = ssrc_id;
2904 ssrc_infos->push_back(info);
2905 ssrc_info = ssrc_infos->end() - 1;
2906 }
2907
2908 // Store the info to the |ssrc_info|.
2909 if (attribute == kSsrcAttributeCname) {
2910 // RFC 5576
2911 // cname:<value>
2912 ssrc_info->cname = value;
2913 } else if (attribute == kSsrcAttributeMsid) {
2914 // draft-alvestrand-mmusic-msid-00
2915 // "msid:" identifier [ " " appdata ]
2916 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002917 rtc::split(value, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002918 if (fields.size() < 1 || fields.size() > 2) {
2919 return ParseFailed(line,
2920 "Expected format \"msid:<identifier>[ <appdata>]\".",
2921 error);
2922 }
deadbeef9d3584c2016-02-16 17:54:10 -08002923 ssrc_info->stream_id = fields[0];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002924 if (fields.size() == 2) {
deadbeef9d3584c2016-02-16 17:54:10 -08002925 ssrc_info->track_id = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002926 }
2927 } else if (attribute == kSsrcAttributeMslabel) {
2928 // draft-alvestrand-rtcweb-mid-01
2929 // mslabel:<value>
2930 ssrc_info->mslabel = value;
2931 } else if (attribute == kSSrcAttributeLabel) {
2932 // The label isn't defined.
2933 // label:<value>
2934 ssrc_info->label = value;
2935 }
2936 return true;
2937}
2938
2939bool ParseSsrcGroupAttribute(const std::string& line,
2940 SsrcGroupVec* ssrc_groups,
2941 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08002942 RTC_DCHECK(ssrc_groups != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002943 // RFC 5576
2944 // a=ssrc-group:<semantics> <ssrc-id> ...
2945 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002946 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002947 kSdpDelimiterSpace, &fields);
2948 const size_t expected_min_fields = 2;
2949 if (fields.size() < expected_min_fields) {
2950 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2951 }
2952 std::string semantics;
2953 if (!GetValue(fields[0], kAttributeSsrcGroup, &semantics, error)) {
2954 return false;
2955 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002956 std::vector<uint32_t> ssrcs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002957 for (size_t i = 1; i < fields.size(); ++i) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02002958 uint32_t ssrc = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002959 if (!GetValueFromString(line, fields[i], &ssrc, error)) {
2960 return false;
2961 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002962 ssrcs.push_back(ssrc);
2963 }
2964 ssrc_groups->push_back(SsrcGroup(semantics, ssrcs));
2965 return true;
2966}
2967
2968bool ParseCryptoAttribute(const std::string& line,
2969 MediaContentDescription* media_desc,
2970 SdpParseError* error) {
2971 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002972 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002973 kSdpDelimiterSpace, &fields);
2974 // RFC 4568
2975 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
2976 const size_t expected_min_fields = 3;
2977 if (fields.size() < expected_min_fields) {
2978 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2979 }
2980 std::string tag_value;
2981 if (!GetValue(fields[0], kAttributeCrypto, &tag_value, error)) {
2982 return false;
2983 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002984 int tag = 0;
2985 if (!GetValueFromString(line, tag_value, &tag, error)) {
2986 return false;
2987 }
jbauch083b73f2015-07-16 02:46:32 -07002988 const std::string& crypto_suite = fields[1];
2989 const std::string& key_params = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002990 std::string session_params;
2991 if (fields.size() > 3) {
2992 session_params = fields[3];
2993 }
2994 media_desc->AddCrypto(CryptoParams(tag, crypto_suite, key_params,
2995 session_params));
2996 return true;
2997}
2998
2999// Updates or creates a new codec entry in the audio description with according
deadbeef67cf2c12016-04-13 10:07:16 -07003000// to |name|, |clockrate|, |bitrate|, and |channels|.
3001void UpdateCodec(int payload_type,
3002 const std::string& name,
3003 int clockrate,
3004 int bitrate,
3005 size_t channels,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003006 AudioContentDescription* audio_desc) {
3007 // Codec may already be populated with (only) optional parameters
3008 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00003009 cricket::AudioCodec codec =
3010 GetCodecWithPayloadType(audio_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003011 codec.name = name;
3012 codec.clockrate = clockrate;
3013 codec.bitrate = bitrate;
3014 codec.channels = channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003015 AddOrReplaceCodec<AudioContentDescription, cricket::AudioCodec>(audio_desc,
3016 codec);
3017}
3018
3019// Updates or creates a new codec entry in the video description according to
deadbeef67cf2c12016-04-13 10:07:16 -07003020// |name|, |width|, |height|, and |framerate|.
3021void UpdateCodec(int payload_type,
3022 const std::string& name,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003023 VideoContentDescription* video_desc) {
3024 // Codec may already be populated with (only) optional parameters
3025 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00003026 cricket::VideoCodec codec =
3027 GetCodecWithPayloadType(video_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003028 codec.name = name;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003029 AddOrReplaceCodec<VideoContentDescription, cricket::VideoCodec>(video_desc,
3030 codec);
3031}
3032
3033bool ParseRtpmapAttribute(const std::string& line,
3034 const MediaType media_type,
deadbeef67cf2c12016-04-13 10:07:16 -07003035 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003036 MediaContentDescription* media_desc,
3037 SdpParseError* error) {
3038 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003039 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003040 kSdpDelimiterSpace, &fields);
3041 // RFC 4566
3042 // a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encodingparameters>]
3043 const size_t expected_min_fields = 2;
3044 if (fields.size() < expected_min_fields) {
3045 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
3046 }
3047 std::string payload_type_value;
3048 if (!GetValue(fields[0], kAttributeRtpmap, &payload_type_value, error)) {
3049 return false;
3050 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003051 int payload_type = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00003052 if (!GetPayloadTypeFromString(line, payload_type_value, &payload_type,
3053 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003054 return false;
3055 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003056
deadbeef67cf2c12016-04-13 10:07:16 -07003057 if (std::find(payload_types.begin(), payload_types.end(), payload_type) ==
3058 payload_types.end()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003059 LOG(LS_WARNING) << "Ignore rtpmap line that did not appear in the "
3060 << "<fmt> of the m-line: " << line;
3061 return true;
3062 }
jbauch083b73f2015-07-16 02:46:32 -07003063 const std::string& encoder = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003064 std::vector<std::string> codec_params;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003065 rtc::split(encoder, '/', &codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003066 // <encoding name>/<clock rate>[/<encodingparameters>]
3067 // 2 mandatory fields
3068 if (codec_params.size() < 2 || codec_params.size() > 3) {
3069 return ParseFailed(line,
3070 "Expected format \"<encoding name>/<clock rate>"
3071 "[/<encodingparameters>]\".",
3072 error);
3073 }
jbauch083b73f2015-07-16 02:46:32 -07003074 const std::string& encoding_name = codec_params[0];
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003075 int clock_rate = 0;
3076 if (!GetValueFromString(line, codec_params[1], &clock_rate, error)) {
3077 return false;
3078 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003079 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3080 VideoContentDescription* video_desc =
3081 static_cast<VideoContentDescription*>(media_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003082 UpdateCodec(payload_type, encoding_name,
deadbeef67cf2c12016-04-13 10:07:16 -07003083 video_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003084 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3085 // RFC 4566
3086 // For audio streams, <encoding parameters> indicates the number
3087 // of audio channels. This parameter is OPTIONAL and may be
3088 // omitted if the number of channels is one, provided that no
3089 // additional parameters are needed.
Peter Kasting69558702016-01-12 16:26:35 -08003090 size_t channels = 1;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003091 if (codec_params.size() == 3) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003092 if (!GetValueFromString(line, codec_params[2], &channels, error)) {
3093 return false;
3094 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003095 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003096 AudioContentDescription* audio_desc =
3097 static_cast<AudioContentDescription*>(media_desc);
ossue1405ad2017-01-23 08:55:48 -08003098 UpdateCodec(payload_type, encoding_name, clock_rate, 0, channels,
deadbeef67cf2c12016-04-13 10:07:16 -07003099 audio_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003100 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
3101 DataContentDescription* data_desc =
3102 static_cast<DataContentDescription*>(media_desc);
deadbeef67cf2c12016-04-13 10:07:16 -07003103 data_desc->AddCodec(cricket::DataCodec(payload_type, encoding_name));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003104 }
3105 return true;
3106}
3107
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003108bool ParseFmtpParam(const std::string& line, std::string* parameter,
3109 std::string* value, SdpParseError* error) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003110 if (!rtc::tokenize_first(line, kSdpDelimiterEqual, parameter, value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003111 ParseFailed(line, "Unable to parse fmtp parameter. \'=\' missing.", error);
3112 return false;
3113 }
3114 // a=fmtp:<payload_type> <param1>=<value1>; <param2>=<value2>; ...
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003115 return true;
3116}
3117
3118bool ParseFmtpAttributes(const std::string& line, const MediaType media_type,
3119 MediaContentDescription* media_desc,
3120 SdpParseError* error) {
3121 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3122 media_type != cricket::MEDIA_TYPE_VIDEO) {
3123 return true;
3124 }
Donald Curtis0e07f922015-05-15 09:21:23 -07003125
3126 std::string line_payload;
3127 std::string line_params;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003128
3129 // RFC 5576
3130 // a=fmtp:<format> <format specific parameters>
3131 // At least two fields, whereas the second one is any of the optional
3132 // parameters.
Donald Curtis144d0182015-05-15 13:14:24 -07003133 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
3134 &line_payload, &line_params)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003135 ParseFailedExpectMinFieldNum(line, 2, error);
3136 return false;
3137 }
3138
Donald Curtis0e07f922015-05-15 09:21:23 -07003139 // Parse out the payload information.
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003140 std::string payload_type_str;
Donald Curtis0e07f922015-05-15 09:21:23 -07003141 if (!GetValue(line_payload, kAttributeFmtp, &payload_type_str, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003142 return false;
3143 }
3144
Donald Curtis0e07f922015-05-15 09:21:23 -07003145 int payload_type = 0;
Donald Curtis144d0182015-05-15 13:14:24 -07003146 if (!GetPayloadTypeFromString(line_payload, payload_type_str, &payload_type,
3147 error)) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003148 return false;
3149 }
3150
3151 // Parse out format specific parameters.
3152 std::vector<std::string> fields;
3153 rtc::split(line_params, kSdpDelimiterSemicolon, &fields);
3154
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003155 cricket::CodecParameterMap codec_params;
Donald Curtis144d0182015-05-15 13:14:24 -07003156 for (auto& iter : fields) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003157 if (iter.find(kSdpDelimiterEqual) == std::string::npos) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003158 // Only fmtps with equals are currently supported. Other fmtp types
3159 // should be ignored. Unknown fmtps do not constitute an error.
3160 continue;
3161 }
Donald Curtis0e07f922015-05-15 09:21:23 -07003162
3163 std::string name;
3164 std::string value;
3165 if (!ParseFmtpParam(rtc::string_trim(iter), &name, &value, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003166 return false;
3167 }
3168 codec_params[name] = value;
3169 }
3170
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003171 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3172 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003173 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003174 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3175 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003176 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003177 }
3178 return true;
3179}
3180
3181bool ParseRtcpFbAttribute(const std::string& line, const MediaType media_type,
3182 MediaContentDescription* media_desc,
3183 SdpParseError* error) {
3184 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3185 media_type != cricket::MEDIA_TYPE_VIDEO) {
3186 return true;
3187 }
3188 std::vector<std::string> rtcp_fb_fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003189 rtc::split(line.c_str(), kSdpDelimiterSpace, &rtcp_fb_fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003190 if (rtcp_fb_fields.size() < 2) {
3191 return ParseFailedGetValue(line, kAttributeRtcpFb, error);
3192 }
3193 std::string payload_type_string;
3194 if (!GetValue(rtcp_fb_fields[0], kAttributeRtcpFb, &payload_type_string,
3195 error)) {
3196 return false;
3197 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003198 int payload_type = kWildcardPayloadType;
3199 if (payload_type_string != "*") {
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00003200 if (!GetPayloadTypeFromString(line, payload_type_string, &payload_type,
3201 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003202 return false;
3203 }
3204 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003205 std::string id = rtcp_fb_fields[1];
3206 std::string param = "";
3207 for (std::vector<std::string>::iterator iter = rtcp_fb_fields.begin() + 2;
3208 iter != rtcp_fb_fields.end(); ++iter) {
3209 param.append(*iter);
3210 }
3211 const cricket::FeedbackParam feedback_param(id, param);
3212
3213 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003214 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
3215 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003216 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003217 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
3218 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003219 }
3220 return true;
3221}
3222
3223} // namespace webrtc