blob: b700d8550e78c4aed316f6f4aab9bee10025d51a [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
kjellanderb24317b2016-02-10 07:54:43 -08002 * Copyright 2011 The WebRTC project authors. All Rights Reserved.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003 *
kjellanderb24317b2016-02-10 07:54:43 -08004 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00009 */
10
Henrik Kjellander15583c12016-02-10 10:53:12 +010011#include "webrtc/api/webrtcsdp.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000012
kjellandera96e2d72016-02-04 23:52:28 -080013#include <ctype.h>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000014#include <limits.h>
15#include <stdio.h>
kwibergd1fe2812016-04-27 06:47:29 -070016
henrike@webrtc.org28e20752013-07-10 00:45:36 +000017#include <algorithm>
kwibergd1fe2812016-04-27 06:47:29 -070018#include <memory>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000019#include <string>
deadbeef67cf2c12016-04-13 10:07:16 -070020#include <unordered_map>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000021#include <vector>
22
Henrik Kjellander15583c12016-02-10 10:53:12 +010023#include "webrtc/api/jsepicecandidate.h"
24#include "webrtc/api/jsepsessiondescription.h"
tfarina5237aaf2015-11-10 23:44:30 -080025#include "webrtc/base/arraysize.h"
buildbot@webrtc.orga09a9992014-08-13 17:26:08 +000026#include "webrtc/base/common.h"
27#include "webrtc/base/logging.h"
28#include "webrtc/base/messagedigest.h"
29#include "webrtc/base/stringutils.h"
isheriff6f8d6862016-05-26 11:24:55 -070030// for RtpExtension
31#include "webrtc/config.h"
kjellandera96e2d72016-02-04 23:52:28 -080032#include "webrtc/media/base/codec.h"
kjellandera96e2d72016-02-04 23:52:28 -080033#include "webrtc/media/base/cryptoparams.h"
kjellanderf4752772016-03-02 05:42:30 -080034#include "webrtc/media/base/mediaconstants.h"
kjellandera96e2d72016-02-04 23:52:28 -080035#include "webrtc/media/base/rtputils.h"
36#include "webrtc/media/sctp/sctpdataengine.h"
37#include "webrtc/p2p/base/candidate.h"
kjellanderf4752772016-03-02 05:42:30 -080038#include "webrtc/p2p/base/p2pconstants.h"
kjellandera96e2d72016-02-04 23:52:28 -080039#include "webrtc/p2p/base/port.h"
kjellander@webrtc.org9b8df252016-02-12 06:47:59 +010040#include "webrtc/pc/mediasession.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000041
42using cricket::AudioContentDescription;
43using cricket::Candidate;
44using cricket::Candidates;
45using cricket::ContentDescription;
46using cricket::ContentInfo;
47using cricket::CryptoParams;
48using cricket::DataContentDescription;
49using cricket::ICE_CANDIDATE_COMPONENT_RTP;
50using cricket::ICE_CANDIDATE_COMPONENT_RTCP;
51using cricket::kCodecParamMaxBitrate;
52using cricket::kCodecParamMaxPTime;
53using cricket::kCodecParamMaxQuantization;
54using cricket::kCodecParamMinBitrate;
55using cricket::kCodecParamMinPTime;
56using cricket::kCodecParamPTime;
57using cricket::kCodecParamSPropStereo;
buildbot@webrtc.orged97bb02014-05-07 11:15:20 +000058using cricket::kCodecParamStartBitrate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000059using cricket::kCodecParamStereo;
60using cricket::kCodecParamUseInbandFec;
Minyue Li7100dcd2015-03-27 05:05:59 +010061using cricket::kCodecParamUseDtx;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000062using cricket::kCodecParamSctpProtocol;
63using cricket::kCodecParamSctpStreams;
henrike@webrtc.org1e09a712013-07-26 19:17:59 +000064using cricket::kCodecParamMaxAverageBitrate;
buildbot@webrtc.org5d639b32014-09-10 07:57:12 +000065using cricket::kCodecParamMaxPlaybackRate;
stefan@webrtc.org85d27942014-06-09 12:51:39 +000066using cricket::kCodecParamAssociatedPayloadType;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000067using cricket::MediaContentDescription;
68using cricket::MediaType;
isheriff6f8d6862016-05-26 11:24:55 -070069using cricket::RtpHeaderExtensions;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000070using cricket::SsrcGroup;
71using cricket::StreamParams;
72using cricket::StreamParamsVec;
73using cricket::TransportDescription;
74using cricket::TransportInfo;
75using cricket::VideoContentDescription;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000076using rtc::SocketAddress;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000077
henrike@webrtc.org28e20752013-07-10 00:45:36 +000078namespace cricket {
79class SessionDescription;
80}
81
82namespace webrtc {
83
84// Line type
85// RFC 4566
86// An SDP session description consists of a number of lines of text of
87// the form:
88// <type>=<value>
89// where <type> MUST be exactly one case-significant character.
deadbeef9d3584c2016-02-16 17:54:10 -080090static const int kLinePrefixLength = 2; // Length of <type>=
henrike@webrtc.org28e20752013-07-10 00:45:36 +000091static const char kLineTypeVersion = 'v';
92static const char kLineTypeOrigin = 'o';
93static const char kLineTypeSessionName = 's';
94static const char kLineTypeSessionInfo = 'i';
95static const char kLineTypeSessionUri = 'u';
96static const char kLineTypeSessionEmail = 'e';
97static const char kLineTypeSessionPhone = 'p';
98static const char kLineTypeSessionBandwidth = 'b';
99static const char kLineTypeTiming = 't';
100static const char kLineTypeRepeatTimes = 'r';
101static const char kLineTypeTimeZone = 'z';
102static const char kLineTypeEncryptionKey = 'k';
103static const char kLineTypeMedia = 'm';
104static const char kLineTypeConnection = 'c';
105static const char kLineTypeAttributes = 'a';
106
107// Attributes
108static const char kAttributeGroup[] = "group";
109static const char kAttributeMid[] = "mid";
deadbeef9d3584c2016-02-16 17:54:10 -0800110static const char kAttributeMsid[] = "msid";
deadbeef25ed4352016-12-12 18:37:36 -0800111static const char kAttributeBundleOnly[] = "bundle-only";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000112static const char kAttributeRtcpMux[] = "rtcp-mux";
deadbeef13871492015-12-09 12:37:51 -0800113static const char kAttributeRtcpReducedSize[] = "rtcp-rsize";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000114static const char kAttributeSsrc[] = "ssrc";
115static const char kSsrcAttributeCname[] = "cname";
116static const char kAttributeExtmap[] = "extmap";
117// draft-alvestrand-mmusic-msid-01
118// a=msid-semantic: WMS
119static const char kAttributeMsidSemantics[] = "msid-semantic";
120static const char kMediaStreamSemantic[] = "WMS";
121static const char kSsrcAttributeMsid[] = "msid";
122static const char kDefaultMsid[] = "default";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000123static const char kSsrcAttributeMslabel[] = "mslabel";
124static const char kSSrcAttributeLabel[] = "label";
125static const char kAttributeSsrcGroup[] = "ssrc-group";
126static const char kAttributeCrypto[] = "crypto";
127static const char kAttributeCandidate[] = "candidate";
128static const char kAttributeCandidateTyp[] = "typ";
129static const char kAttributeCandidateRaddr[] = "raddr";
130static const char kAttributeCandidateRport[] = "rport";
honghaiza54a0802015-12-16 18:37:23 -0800131static const char kAttributeCandidateUfrag[] = "ufrag";
132static const char kAttributeCandidatePwd[] = "pwd";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000133static const char kAttributeCandidateGeneration[] = "generation";
honghaiza0c44ea2016-03-23 16:07:48 -0700134static const char kAttributeCandidateNetworkId[] = "network-id";
honghaize1a0c942016-02-16 14:54:56 -0800135static const char kAttributeCandidateNetworkCost[] = "network-cost";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000136static const char kAttributeFingerprint[] = "fingerprint";
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000137static const char kAttributeSetup[] = "setup";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000138static const char kAttributeFmtp[] = "fmtp";
139static const char kAttributeRtpmap[] = "rtpmap";
wu@webrtc.org78187522013-10-07 23:32:02 +0000140static const char kAttributeSctpmap[] = "sctpmap";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000141static const char kAttributeRtcp[] = "rtcp";
142static const char kAttributeIceUfrag[] = "ice-ufrag";
143static const char kAttributeIcePwd[] = "ice-pwd";
144static const char kAttributeIceLite[] = "ice-lite";
145static const char kAttributeIceOption[] = "ice-options";
146static const char kAttributeSendOnly[] = "sendonly";
147static const char kAttributeRecvOnly[] = "recvonly";
148static const char kAttributeRtcpFb[] = "rtcp-fb";
149static const char kAttributeSendRecv[] = "sendrecv";
150static const char kAttributeInactive[] = "inactive";
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +0000151// draft-ietf-mmusic-sctp-sdp-07
152// a=sctp-port
153static const char kAttributeSctpPort[] = "sctp-port";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000154
155// Experimental flags
156static const char kAttributeXGoogleFlag[] = "x-google-flag";
157static const char kValueConference[] = "conference";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000158
159// Candidate
160static const char kCandidateHost[] = "host";
161static const char kCandidateSrflx[] = "srflx";
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700162static const char kCandidatePrflx[] = "prflx";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000163static const char kCandidateRelay[] = "relay";
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +0000164static const char kTcpCandidateType[] = "tcptype";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000165
166static const char kSdpDelimiterEqual = '=';
167static const char kSdpDelimiterSpace = ' ';
168static const char kSdpDelimiterColon = ':';
169static const char kSdpDelimiterSemicolon = ';';
170static const char kSdpDelimiterSlash = '/';
171static const char kNewLine = '\n';
172static const char kReturn = '\r';
173static const char kLineBreak[] = "\r\n";
174
175// TODO: Generate the Session and Time description
176// instead of hardcoding.
177static const char kSessionVersion[] = "v=0";
178// RFC 4566
179static const char kSessionOriginUsername[] = "-";
180static const char kSessionOriginSessionId[] = "0";
181static const char kSessionOriginSessionVersion[] = "0";
182static const char kSessionOriginNettype[] = "IN";
183static const char kSessionOriginAddrtype[] = "IP4";
184static const char kSessionOriginAddress[] = "127.0.0.1";
185static const char kSessionName[] = "s=-";
186static const char kTimeDescription[] = "t=0 0";
187static const char kAttrGroup[] = "a=group:BUNDLE";
188static const char kConnectionNettype[] = "IN";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000189static const char kConnectionIpv4Addrtype[] = "IP4";
190static const char kConnectionIpv6Addrtype[] = "IP6";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000191static const char kMediaTypeVideo[] = "video";
192static const char kMediaTypeAudio[] = "audio";
193static const char kMediaTypeData[] = "application";
194static const char kMediaPortRejected[] = "0";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000195// draft-ietf-mmusic-trickle-ice-01
196// When no candidates have been gathered, set the connection
197// address to IP6 ::.
perkj@webrtc.orgd105cc82014-11-07 11:22:06 +0000198// TODO(perkj): FF can not parse IP6 ::. See http://crbug/430333
199// Use IPV4 per default.
200static const char kDummyAddress[] = "0.0.0.0";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000201static const char kDummyPort[] = "9";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000202// RFC 3556
203static const char kApplicationSpecificMaximum[] = "AS";
204
205static const int kDefaultVideoClockrate = 90000;
206
207// ISAC special-case.
208static const char kIsacCodecName[] = "ISAC"; // From webrtcvoiceengine.cc
209static const int kIsacWbDefaultRate = 32000; // From acm_common_defs.h
210static const int kIsacSwbDefaultRate = 56000; // From acm_common_defs.h
211
wu@webrtc.org78187522013-10-07 23:32:02 +0000212static const char kDefaultSctpmapProtocol[] = "webrtc-datachannel";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000213
pkasting@chromium.orgd3245462015-02-23 21:28:22 +0000214// RTP payload type is in the 0-127 range. Use -1 to indicate "all" payload
215// types.
216const int kWildcardPayloadType = -1;
217
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000218struct SsrcInfo {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200219 uint32_t ssrc_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000220 std::string cname;
deadbeef9d3584c2016-02-16 17:54:10 -0800221 std::string stream_id;
222 std::string track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000223
224 // For backward compatibility.
225 // TODO(ronghuawu): Remove below 2 fields once all the clients support msid.
226 std::string label;
227 std::string mslabel;
228};
229typedef std::vector<SsrcInfo> SsrcInfoVec;
230typedef std::vector<SsrcGroup> SsrcGroupVec;
231
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000232template <class T>
233static void AddFmtpLine(const T& codec, std::string* message);
234static void BuildMediaDescription(const ContentInfo* content_info,
235 const TransportInfo* transport_info,
236 const MediaType media_type,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000237 const std::vector<Candidate>& candidates,
deadbeef9d3584c2016-02-16 17:54:10 -0800238 bool unified_plan_sdp,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000239 std::string* message);
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000240static void BuildSctpContentAttributes(std::string* message, int sctp_port);
deadbeef9d3584c2016-02-16 17:54:10 -0800241static void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
242 const MediaType media_type,
243 bool unified_plan_sdp,
244 std::string* message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000245static void BuildRtpMap(const MediaContentDescription* media_desc,
246 const MediaType media_type,
247 std::string* message);
248static void BuildCandidate(const std::vector<Candidate>& candidates,
honghaiza54a0802015-12-16 18:37:23 -0800249 bool include_ufrag,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000250 std::string* message);
251static void BuildIceOptions(const std::vector<std::string>& transport_options,
252 std::string* message);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +0000253static bool IsRtp(const std::string& protocol);
254static bool IsDtlsSctp(const std::string& protocol);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000255static bool ParseSessionDescription(const std::string& message, size_t* pos,
256 std::string* session_id,
257 std::string* session_version,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000258 TransportDescription* session_td,
259 RtpHeaderExtensions* session_extmaps,
260 cricket::SessionDescription* desc,
261 SdpParseError* error);
262static bool ParseGroupAttribute(const std::string& line,
263 cricket::SessionDescription* desc,
264 SdpParseError* error);
265static bool ParseMediaDescription(
266 const std::string& message,
267 const TransportDescription& session_td,
268 const RtpHeaderExtensions& session_extmaps,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000269 size_t* pos, cricket::SessionDescription* desc,
270 std::vector<JsepIceCandidate*>* candidates,
271 SdpParseError* error);
272static bool ParseContent(const std::string& message,
273 const MediaType media_type,
274 int mline_index,
275 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -0700276 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000277 size_t* pos,
278 std::string* content_name,
deadbeef25ed4352016-12-12 18:37:36 -0800279 bool* bundle_only,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000280 MediaContentDescription* media_desc,
281 TransportDescription* transport,
282 std::vector<JsepIceCandidate*>* candidates,
283 SdpParseError* error);
284static bool ParseSsrcAttribute(const std::string& line,
285 SsrcInfoVec* ssrc_infos,
286 SdpParseError* error);
287static bool ParseSsrcGroupAttribute(const std::string& line,
288 SsrcGroupVec* ssrc_groups,
289 SdpParseError* error);
290static bool ParseCryptoAttribute(const std::string& line,
291 MediaContentDescription* media_desc,
292 SdpParseError* error);
293static bool ParseRtpmapAttribute(const std::string& line,
294 const MediaType media_type,
deadbeef67cf2c12016-04-13 10:07:16 -0700295 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000296 MediaContentDescription* media_desc,
297 SdpParseError* error);
298static bool ParseFmtpAttributes(const std::string& line,
299 const MediaType media_type,
300 MediaContentDescription* media_desc,
301 SdpParseError* error);
302static bool ParseFmtpParam(const std::string& line, std::string* parameter,
303 std::string* value, SdpParseError* error);
304static bool ParseCandidate(const std::string& message, Candidate* candidate,
305 SdpParseError* error, bool is_raw);
306static bool ParseRtcpFbAttribute(const std::string& line,
307 const MediaType media_type,
308 MediaContentDescription* media_desc,
309 SdpParseError* error);
310static bool ParseIceOptions(const std::string& line,
311 std::vector<std::string>* transport_options,
312 SdpParseError* error);
313static bool ParseExtmap(const std::string& line,
isheriff6f8d6862016-05-26 11:24:55 -0700314 RtpExtension* extmap,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000315 SdpParseError* error);
316static bool ParseFingerprintAttribute(const std::string& line,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000317 rtc::SSLFingerprint** fingerprint,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000318 SdpParseError* error);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000319static bool ParseDtlsSetup(const std::string& line,
320 cricket::ConnectionRole* role,
321 SdpParseError* error);
deadbeef9d3584c2016-02-16 17:54:10 -0800322static bool ParseMsidAttribute(const std::string& line,
323 std::string* stream_id,
324 std::string* track_id,
325 SdpParseError* error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000326
327// Helper functions
328
329// Below ParseFailed*** functions output the line that caused the parsing
330// failure and the detailed reason (|description|) of the failure to |error|.
331// The functions always return false so that they can be used directly in the
332// following way when error happens:
333// "return ParseFailed***(...);"
334
335// The line starting at |line_start| of |message| is the failing line.
336// The reason for the failure should be provided in the |description|.
337// An example of a description could be "unknown character".
338static bool ParseFailed(const std::string& message,
339 size_t line_start,
340 const std::string& description,
341 SdpParseError* error) {
342 // Get the first line of |message| from |line_start|.
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000343 std::string first_line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000344 size_t line_end = message.find(kNewLine, line_start);
345 if (line_end != std::string::npos) {
346 if (line_end > 0 && (message.at(line_end - 1) == kReturn)) {
347 --line_end;
348 }
349 first_line = message.substr(line_start, (line_end - line_start));
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000350 } else {
351 first_line = message.substr(line_start);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000352 }
353
354 if (error) {
355 error->line = first_line;
356 error->description = description;
357 }
358 LOG(LS_ERROR) << "Failed to parse: \"" << first_line
359 << "\". Reason: " << description;
360 return false;
361}
362
363// |line| is the failing line. The reason for the failure should be
364// provided in the |description|.
365static bool ParseFailed(const std::string& line,
366 const std::string& description,
367 SdpParseError* error) {
368 return ParseFailed(line, 0, description, error);
369}
370
371// Parses failure where the failing SDP line isn't know or there are multiple
372// failing lines.
373static bool ParseFailed(const std::string& description,
374 SdpParseError* error) {
375 return ParseFailed("", description, error);
376}
377
378// |line| is the failing line. The failure is due to the fact that |line|
379// doesn't have |expected_fields| fields.
380static bool ParseFailedExpectFieldNum(const std::string& line,
381 int expected_fields,
382 SdpParseError* error) {
383 std::ostringstream description;
384 description << "Expects " << expected_fields << " fields.";
385 return ParseFailed(line, description.str(), error);
386}
387
388// |line| is the failing line. The failure is due to the fact that |line| has
389// less than |expected_min_fields| fields.
390static bool ParseFailedExpectMinFieldNum(const std::string& line,
391 int expected_min_fields,
392 SdpParseError* error) {
393 std::ostringstream description;
394 description << "Expects at least " << expected_min_fields << " fields.";
395 return ParseFailed(line, description.str(), error);
396}
397
398// |line| is the failing line. The failure is due to the fact that it failed to
399// get the value of |attribute|.
400static bool ParseFailedGetValue(const std::string& line,
401 const std::string& attribute,
402 SdpParseError* error) {
403 std::ostringstream description;
404 description << "Failed to get the value of attribute: " << attribute;
405 return ParseFailed(line, description.str(), error);
406}
407
408// The line starting at |line_start| of |message| is the failing line. The
409// failure is due to the line type (e.g. the "m" part of the "m-line")
410// not matching what is expected. The expected line type should be
411// provided as |line_type|.
412static bool ParseFailedExpectLine(const std::string& message,
413 size_t line_start,
414 const char line_type,
415 const std::string& line_value,
416 SdpParseError* error) {
417 std::ostringstream description;
418 description << "Expect line: " << line_type << "=" << line_value;
419 return ParseFailed(message, line_start, description.str(), error);
420}
421
422static bool AddLine(const std::string& line, std::string* message) {
423 if (!message)
424 return false;
425
426 message->append(line);
427 message->append(kLineBreak);
428 return true;
429}
430
431static bool GetLine(const std::string& message,
432 size_t* pos,
433 std::string* line) {
434 size_t line_begin = *pos;
435 size_t line_end = message.find(kNewLine, line_begin);
436 if (line_end == std::string::npos) {
437 return false;
438 }
439 // Update the new start position
440 *pos = line_end + 1;
441 if (line_end > 0 && (message.at(line_end - 1) == kReturn)) {
442 --line_end;
443 }
444 *line = message.substr(line_begin, (line_end - line_begin));
445 const char* cline = line->c_str();
446 // RFC 4566
447 // An SDP session description consists of a number of lines of text of
448 // the form:
449 // <type>=<value>
450 // where <type> MUST be exactly one case-significant character and
451 // <value> is structured text whose format depends on <type>.
452 // Whitespace MUST NOT be used on either side of the "=" sign.
decurtis@webrtc.org8af11042015-01-07 19:15:51 +0000453 if (line->length() < 3 ||
454 !islower(cline[0]) ||
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000455 cline[1] != kSdpDelimiterEqual ||
456 cline[2] == kSdpDelimiterSpace) {
457 *pos = line_begin;
458 return false;
459 }
460 return true;
461}
462
463// Init |os| to "|type|=|value|".
464static void InitLine(const char type,
465 const std::string& value,
466 std::ostringstream* os) {
467 os->str("");
468 *os << type << kSdpDelimiterEqual << value;
469}
470
471// Init |os| to "a=|attribute|".
472static void InitAttrLine(const std::string& attribute, std::ostringstream* os) {
473 InitLine(kLineTypeAttributes, attribute, os);
474}
475
476// Writes a SDP attribute line based on |attribute| and |value| to |message|.
477static void AddAttributeLine(const std::string& attribute, int value,
478 std::string* message) {
479 std::ostringstream os;
480 InitAttrLine(attribute, &os);
481 os << kSdpDelimiterColon << value;
482 AddLine(os.str(), message);
483}
484
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000485static bool IsLineType(const std::string& message,
486 const char type,
487 size_t line_start) {
488 if (message.size() < line_start + kLinePrefixLength) {
489 return false;
490 }
491 const char* cmessage = message.c_str();
492 return (cmessage[line_start] == type &&
493 cmessage[line_start + 1] == kSdpDelimiterEqual);
494}
495
496static bool IsLineType(const std::string& line,
497 const char type) {
498 return IsLineType(line, type, 0);
499}
500
501static bool GetLineWithType(const std::string& message, size_t* pos,
502 std::string* line, const char type) {
503 if (!IsLineType(message, type, *pos)) {
504 return false;
505 }
506
507 if (!GetLine(message, pos, line))
508 return false;
509
510 return true;
511}
512
513static bool HasAttribute(const std::string& line,
514 const std::string& attribute) {
515 return (line.compare(kLinePrefixLength, attribute.size(), attribute) == 0);
516}
517
Peter Boström0c4e06b2015-10-07 12:23:21 +0200518static bool AddSsrcLine(uint32_t ssrc_id,
519 const std::string& attribute,
520 const std::string& value,
521 std::string* message) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000522 // RFC 5576
523 // a=ssrc:<ssrc-id> <attribute>:<value>
524 std::ostringstream os;
525 InitAttrLine(kAttributeSsrc, &os);
526 os << kSdpDelimiterColon << ssrc_id << kSdpDelimiterSpace
527 << attribute << kSdpDelimiterColon << value;
528 return AddLine(os.str(), message);
529}
530
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000531// Get value only from <attribute>:<value>.
532static bool GetValue(const std::string& message, const std::string& attribute,
533 std::string* value, SdpParseError* error) {
534 std::string leftpart;
Donald Curtis0e07f922015-05-15 09:21:23 -0700535 if (!rtc::tokenize_first(message, kSdpDelimiterColon, &leftpart, value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000536 return ParseFailedGetValue(message, attribute, error);
537 }
538 // The left part should end with the expected attribute.
539 if (leftpart.length() < attribute.length() ||
540 leftpart.compare(leftpart.length() - attribute.length(),
541 attribute.length(), attribute) != 0) {
542 return ParseFailedGetValue(message, attribute, error);
543 }
544 return true;
545}
546
547static bool CaseInsensitiveFind(std::string str1, std::string str2) {
548 std::transform(str1.begin(), str1.end(), str1.begin(),
549 ::tolower);
550 std::transform(str2.begin(), str2.end(), str2.begin(),
551 ::tolower);
552 return str1.find(str2) != std::string::npos;
553}
554
wu@webrtc.org5e760e72014-04-02 23:19:09 +0000555template <class T>
556static bool GetValueFromString(const std::string& line,
557 const std::string& s,
558 T* t,
559 SdpParseError* error) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000560 if (!rtc::FromString(s, t)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +0000561 std::ostringstream description;
562 description << "Invalid value: " << s << ".";
563 return ParseFailed(line, description.str(), error);
564 }
565 return true;
566}
567
pkasting@chromium.orge9facf82015-02-17 20:36:28 +0000568static bool GetPayloadTypeFromString(const std::string& line,
569 const std::string& s,
570 int* payload_type,
571 SdpParseError* error) {
572 return GetValueFromString(line, s, payload_type, error) &&
573 cricket::IsValidRtpPayloadType(*payload_type);
574}
575
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800576// |msid_stream_id| and |msid_track_id| represent the stream/track ID from the
577// "a=msid" attribute, if it exists. They are empty if the attribute does not
578// exist.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000579void CreateTracksFromSsrcInfos(const SsrcInfoVec& ssrc_infos,
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800580 const std::string& msid_stream_id,
581 const std::string& msid_track_id,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000582 StreamParamsVec* tracks) {
583 ASSERT(tracks != NULL);
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800584 ASSERT(msid_stream_id.empty() == msid_track_id.empty());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000585 for (SsrcInfoVec::const_iterator ssrc_info = ssrc_infos.begin();
586 ssrc_info != ssrc_infos.end(); ++ssrc_info) {
587 if (ssrc_info->cname.empty()) {
588 continue;
589 }
590
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800591 std::string stream_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000592 std::string track_id;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800593 if (ssrc_info->stream_id.empty() && !ssrc_info->mslabel.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000594 // If there's no msid and there's mslabel, we consider this is a sdp from
595 // a older version of client that doesn't support msid.
596 // In that case, we use the mslabel and label to construct the track.
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800597 stream_id = ssrc_info->mslabel;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000598 track_id = ssrc_info->label;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800599 } else if (ssrc_info->stream_id.empty() && !msid_stream_id.empty()) {
600 // If there's no msid in the SSRC attributes, but there's a global one
601 // (from a=msid), use that. This is the case with unified plan SDP.
602 stream_id = msid_stream_id;
603 track_id = msid_track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000604 } else {
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800605 stream_id = ssrc_info->stream_id;
deadbeef9d3584c2016-02-16 17:54:10 -0800606 track_id = ssrc_info->track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000607 }
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800608 // If a stream/track ID wasn't populated from the SSRC attributes OR the
609 // msid attribute, use default/random values.
610 if (stream_id.empty()) {
611 stream_id = kDefaultMsid;
612 }
613 if (track_id.empty()) {
614 // TODO(ronghuawu): What should we do if the track id doesn't appear?
615 // Create random string (which will be used as track label later)?
616 track_id = rtc::CreateRandomString(8);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000617 }
618
619 StreamParamsVec::iterator track = tracks->begin();
620 for (; track != tracks->end(); ++track) {
621 if (track->id == track_id) {
622 break;
623 }
624 }
625 if (track == tracks->end()) {
626 // If we don't find an existing track, create a new one.
627 tracks->push_back(StreamParams());
628 track = tracks->end() - 1;
629 }
630 track->add_ssrc(ssrc_info->ssrc_id);
631 track->cname = ssrc_info->cname;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800632 track->sync_label = stream_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000633 track->id = track_id;
634 }
635}
636
637void GetMediaStreamLabels(const ContentInfo* content,
638 std::set<std::string>* labels) {
639 const MediaContentDescription* media_desc =
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000640 static_cast<const MediaContentDescription*>(
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000641 content->description);
642 const cricket::StreamParamsVec& streams = media_desc->streams();
643 for (cricket::StreamParamsVec::const_iterator it = streams.begin();
644 it != streams.end(); ++it) {
645 labels->insert(it->sync_label);
646 }
647}
648
649// RFC 5245
650// It is RECOMMENDED that default candidates be chosen based on the
651// likelihood of those candidates to work with the peer that is being
652// contacted. It is RECOMMENDED that relayed > reflexive > host.
653static const int kPreferenceUnknown = 0;
654static const int kPreferenceHost = 1;
655static const int kPreferenceReflexive = 2;
656static const int kPreferenceRelayed = 3;
657
658static int GetCandidatePreferenceFromType(const std::string& type) {
659 int preference = kPreferenceUnknown;
660 if (type == cricket::LOCAL_PORT_TYPE) {
661 preference = kPreferenceHost;
662 } else if (type == cricket::STUN_PORT_TYPE) {
663 preference = kPreferenceReflexive;
664 } else if (type == cricket::RELAY_PORT_TYPE) {
665 preference = kPreferenceRelayed;
666 } else {
667 ASSERT(false);
668 }
669 return preference;
670}
671
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000672// Get ip and port of the default destination from the |candidates| with the
673// given value of |component_id|. The default candidate should be the one most
674// likely to work, typically IPv4 relay.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000675// RFC 5245
676// The value of |component_id| currently supported are 1 (RTP) and 2 (RTCP).
677// TODO: Decide the default destination in webrtcsession and
678// pass it down via SessionDescription.
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000679static void GetDefaultDestination(
680 const std::vector<Candidate>& candidates,
681 int component_id, std::string* port,
682 std::string* ip, std::string* addr_type) {
perkj@webrtc.orgd105cc82014-11-07 11:22:06 +0000683 *addr_type = kConnectionIpv4Addrtype;
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000684 *port = kDummyPort;
685 *ip = kDummyAddress;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000686 int current_preference = kPreferenceUnknown;
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000687 int current_family = AF_UNSPEC;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000688 for (std::vector<Candidate>::const_iterator it = candidates.begin();
689 it != candidates.end(); ++it) {
690 if (it->component() != component_id) {
691 continue;
692 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000693 // Default destination should be UDP only.
694 if (it->protocol() != cricket::UDP_PROTOCOL_NAME) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000695 continue;
696 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000697 const int preference = GetCandidatePreferenceFromType(it->type());
698 const int family = it->address().ipaddr().family();
699 // See if this candidate is more preferable then the current one if it's the
700 // same family. Or if the current family is IPv4 already so we could safely
701 // ignore all IPv6 ones. WebRTC bug 4269.
702 // http://code.google.com/p/webrtc/issues/detail?id=4269
703 if ((preference <= current_preference && current_family == family) ||
704 (current_family == AF_INET && family == AF_INET6)) {
705 continue;
706 }
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000707 if (family == AF_INET) {
708 addr_type->assign(kConnectionIpv4Addrtype);
709 } else if (family == AF_INET6) {
710 addr_type->assign(kConnectionIpv6Addrtype);
711 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000712 current_preference = preference;
713 current_family = family;
714 *port = it->address().PortAsString();
715 *ip = it->address().ipaddr().ToString();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000716 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000717}
718
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000719// Update |mline|'s default destination and append a c line after it.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000720static void UpdateMediaDefaultDestination(
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000721 const std::vector<Candidate>& candidates,
jbauch083b73f2015-07-16 02:46:32 -0700722 const std::string& mline,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000723 std::string* message) {
724 std::string new_lines;
725 AddLine(mline, &new_lines);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000726 // RFC 4566
727 // m=<media> <port> <proto> <fmt> ...
728 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000729 rtc::split(mline, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000730 if (fields.size() < 3) {
731 return;
732 }
733
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000734 std::ostringstream os;
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000735 std::string rtp_port, rtp_ip, addr_type;
736 GetDefaultDestination(candidates, ICE_CANDIDATE_COMPONENT_RTP,
737 &rtp_port, &rtp_ip, &addr_type);
738 // Found default RTP candidate.
739 // RFC 5245
740 // The default candidates are added to the SDP as the default
741 // destination for media. For streams based on RTP, this is done by
742 // placing the IP address and port of the RTP candidate into the c and m
743 // lines, respectively.
744 // Update the port in the m line.
745 // If this is a m-line with port equal to 0, we don't change it.
746 if (fields[1] != kMediaPortRejected) {
747 new_lines.replace(fields[0].size() + 1,
748 fields[1].size(),
749 rtp_port);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000750 }
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000751 // Add the c line.
752 // RFC 4566
753 // c=<nettype> <addrtype> <connection-address>
754 InitLine(kLineTypeConnection, kConnectionNettype, &os);
755 os << " " << addr_type << " " << rtp_ip;
756 AddLine(os.str(), &new_lines);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000757 message->append(new_lines);
758}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000759
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000760// Gets "a=rtcp" line if found default RTCP candidate from |candidates|.
761static std::string GetRtcpLine(const std::vector<Candidate>& candidates) {
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000762 std::string rtcp_line, rtcp_port, rtcp_ip, addr_type;
763 GetDefaultDestination(candidates, ICE_CANDIDATE_COMPONENT_RTCP,
764 &rtcp_port, &rtcp_ip, &addr_type);
765 // Found default RTCP candidate.
766 // RFC 5245
767 // If the agent is utilizing RTCP, it MUST encode the RTCP candidate
768 // using the a=rtcp attribute as defined in RFC 3605.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000769
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000770 // RFC 3605
771 // rtcp-attribute = "a=rtcp:" port [nettype space addrtype space
772 // connection-address] CRLF
773 std::ostringstream os;
774 InitAttrLine(kAttributeRtcp, &os);
775 os << kSdpDelimiterColon
776 << rtcp_port << " "
777 << kConnectionNettype << " "
778 << addr_type << " "
779 << rtcp_ip;
780 rtcp_line = os.str();
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000781 return rtcp_line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000782}
783
784// Get candidates according to the mline index from SessionDescriptionInterface.
785static void GetCandidatesByMindex(const SessionDescriptionInterface& desci,
786 int mline_index,
787 std::vector<Candidate>* candidates) {
788 if (!candidates) {
789 return;
790 }
791 const IceCandidateCollection* cc = desci.candidates(mline_index);
792 for (size_t i = 0; i < cc->count(); ++i) {
793 const IceCandidateInterface* candidate = cc->at(i);
794 candidates->push_back(candidate->candidate());
795 }
796}
797
deadbeef9d3584c2016-02-16 17:54:10 -0800798std::string SdpSerialize(const JsepSessionDescription& jdesc,
799 bool unified_plan_sdp) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000800 const cricket::SessionDescription* desc = jdesc.description();
801 if (!desc) {
802 return "";
803 }
804
805 std::string message;
806
807 // Session Description.
808 AddLine(kSessionVersion, &message);
809 // Session Origin
810 // RFC 4566
811 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
812 // <unicast-address>
813 std::ostringstream os;
814 InitLine(kLineTypeOrigin, kSessionOriginUsername, &os);
jbauch083b73f2015-07-16 02:46:32 -0700815 const std::string& session_id = jdesc.session_id().empty() ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000816 kSessionOriginSessionId : jdesc.session_id();
jbauch083b73f2015-07-16 02:46:32 -0700817 const std::string& session_version = jdesc.session_version().empty() ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000818 kSessionOriginSessionVersion : jdesc.session_version();
819 os << " " << session_id << " " << session_version << " "
820 << kSessionOriginNettype << " " << kSessionOriginAddrtype << " "
821 << kSessionOriginAddress;
822 AddLine(os.str(), &message);
823 AddLine(kSessionName, &message);
824
825 // Time Description.
826 AddLine(kTimeDescription, &message);
827
828 // Group
829 if (desc->HasGroup(cricket::GROUP_TYPE_BUNDLE)) {
830 std::string group_line = kAttrGroup;
831 const cricket::ContentGroup* group =
832 desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
833 ASSERT(group != NULL);
834 const cricket::ContentNames& content_names = group->content_names();
835 for (cricket::ContentNames::const_iterator it = content_names.begin();
836 it != content_names.end(); ++it) {
837 group_line.append(" ");
838 group_line.append(*it);
839 }
840 AddLine(group_line, &message);
841 }
842
843 // MediaStream semantics
844 InitAttrLine(kAttributeMsidSemantics, &os);
845 os << kSdpDelimiterColon << " " << kMediaStreamSemantic;
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000846
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000847 std::set<std::string> media_stream_labels;
848 const ContentInfo* audio_content = GetFirstAudioContent(desc);
849 if (audio_content)
850 GetMediaStreamLabels(audio_content, &media_stream_labels);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000851
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000852 const ContentInfo* video_content = GetFirstVideoContent(desc);
853 if (video_content)
854 GetMediaStreamLabels(video_content, &media_stream_labels);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000855
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000856 for (std::set<std::string>::const_iterator it =
857 media_stream_labels.begin(); it != media_stream_labels.end(); ++it) {
858 os << " " << *it;
859 }
860 AddLine(os.str(), &message);
861
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000862 // Preserve the order of the media contents.
863 int mline_index = -1;
864 for (cricket::ContentInfos::const_iterator it = desc->contents().begin();
865 it != desc->contents().end(); ++it) {
866 const MediaContentDescription* mdesc =
867 static_cast<const MediaContentDescription*>(it->description);
868 std::vector<Candidate> candidates;
869 GetCandidatesByMindex(jdesc, ++mline_index, &candidates);
deadbeef9d3584c2016-02-16 17:54:10 -0800870 BuildMediaDescription(&*it, desc->GetTransportInfoByName(it->name),
871 mdesc->type(), candidates, unified_plan_sdp,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000872 &message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000873 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000874 return message;
875}
876
877// Serializes the passed in IceCandidateInterface to a SDP string.
878// candidate - The candidate to be serialized.
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700879std::string SdpSerializeCandidate(const IceCandidateInterface& candidate) {
880 return SdpSerializeCandidate(candidate.candidate());
881}
882
883// Serializes a cricket Candidate.
884std::string SdpSerializeCandidate(const cricket::Candidate& candidate) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000885 std::string message;
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700886 std::vector<cricket::Candidate> candidates(1, candidate);
honghaiza54a0802015-12-16 18:37:23 -0800887 BuildCandidate(candidates, true, &message);
wu@webrtc.orgec9f5fb2014-06-24 17:05:10 +0000888 // From WebRTC draft section 4.8.1.1 candidate-attribute will be
889 // just candidate:<candidate> not a=candidate:<blah>CRLF
890 ASSERT(message.find("a=") == 0);
891 message.erase(0, 2);
892 ASSERT(message.find(kLineBreak) == message.size() - 2);
893 message.resize(message.size() - 2);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000894 return message;
895}
896
897bool SdpDeserialize(const std::string& message,
898 JsepSessionDescription* jdesc,
899 SdpParseError* error) {
900 std::string session_id;
901 std::string session_version;
Peter Thatcher7cbd1882015-09-17 18:54:52 -0700902 TransportDescription session_td("", "");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000903 RtpHeaderExtensions session_extmaps;
904 cricket::SessionDescription* desc = new cricket::SessionDescription();
905 std::vector<JsepIceCandidate*> candidates;
906 size_t current_pos = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000907
908 // Session Description
909 if (!ParseSessionDescription(message, &current_pos, &session_id,
deadbeefc80741f2015-10-22 13:14:45 -0700910 &session_version, &session_td, &session_extmaps,
911 desc, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000912 delete desc;
913 return false;
914 }
915
916 // Media Description
deadbeefc80741f2015-10-22 13:14:45 -0700917 if (!ParseMediaDescription(message, session_td, session_extmaps, &current_pos,
918 desc, &candidates, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000919 delete desc;
920 for (std::vector<JsepIceCandidate*>::const_iterator
921 it = candidates.begin(); it != candidates.end(); ++it) {
922 delete *it;
923 }
924 return false;
925 }
926
927 jdesc->Initialize(desc, session_id, session_version);
928
929 for (std::vector<JsepIceCandidate*>::const_iterator
930 it = candidates.begin(); it != candidates.end(); ++it) {
931 jdesc->AddCandidate(*it);
932 delete *it;
933 }
934 return true;
935}
936
937bool SdpDeserializeCandidate(const std::string& message,
938 JsepIceCandidate* jcandidate,
939 SdpParseError* error) {
940 ASSERT(jcandidate != NULL);
941 Candidate candidate;
942 if (!ParseCandidate(message, &candidate, error, true)) {
943 return false;
944 }
945 jcandidate->SetCandidate(candidate);
946 return true;
947}
948
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700949bool SdpDeserializeCandidate(const std::string& transport_name,
950 const std::string& message,
951 cricket::Candidate* candidate,
952 SdpParseError* error) {
953 ASSERT(candidate != nullptr);
954 if (!ParseCandidate(message, candidate, error, true)) {
955 return false;
956 }
957 candidate->set_transport_name(transport_name);
958 return true;
959}
960
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000961bool ParseCandidate(const std::string& message, Candidate* candidate,
962 SdpParseError* error, bool is_raw) {
963 ASSERT(candidate != NULL);
964
965 // Get the first line from |message|.
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000966 std::string first_line = message;
967 size_t pos = 0;
968 GetLine(message, &pos, &first_line);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000969
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000970 // Makes sure |message| contains only one line.
971 if (message.size() > first_line.size()) {
972 std::string left, right;
Donald Curtis0e07f922015-05-15 09:21:23 -0700973 if (rtc::tokenize_first(message, kNewLine, &left, &right) &&
974 !right.empty()) {
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000975 return ParseFailed(message, 0, "Expect one line only", error);
976 }
977 }
978
979 // From WebRTC draft section 4.8.1.1 candidate-attribute should be
980 // candidate:<candidate> when trickled, but we still support
981 // a=candidate:<blah>CRLF for backward compatibility and for parsing a line
982 // from the SDP.
983 if (IsLineType(first_line, kLineTypeAttributes)) {
984 first_line = first_line.substr(kLinePrefixLength);
985 }
986
987 std::string attribute_candidate;
988 std::string candidate_value;
989
990 // |first_line| must be in the form of "candidate:<value>".
Donald Curtis144d0182015-05-15 13:14:24 -0700991 if (!rtc::tokenize_first(first_line, kSdpDelimiterColon, &attribute_candidate,
992 &candidate_value) ||
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000993 attribute_candidate != kAttributeCandidate) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000994 if (is_raw) {
995 std::ostringstream description;
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000996 description << "Expect line: " << kAttributeCandidate
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000997 << ":" << "<candidate-str>";
998 return ParseFailed(first_line, 0, description.str(), error);
999 } else {
1000 return ParseFailedExpectLine(first_line, 0, kLineTypeAttributes,
1001 kAttributeCandidate, error);
1002 }
1003 }
1004
1005 std::vector<std::string> fields;
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001006 rtc::split(candidate_value, kSdpDelimiterSpace, &fields);
1007
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001008 // RFC 5245
1009 // a=candidate:<foundation> <component-id> <transport> <priority>
1010 // <connection-address> <port> typ <candidate-types>
1011 // [raddr <connection-address>] [rport <port>]
1012 // *(SP extension-att-name SP extension-att-value)
1013 const size_t expected_min_fields = 8;
1014 if (fields.size() < expected_min_fields ||
1015 (fields[6] != kAttributeCandidateTyp)) {
1016 return ParseFailedExpectMinFieldNum(first_line, expected_min_fields, error);
1017 }
jbauch083b73f2015-07-16 02:46:32 -07001018 const std::string& foundation = fields[0];
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001019
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001020 int component_id = 0;
1021 if (!GetValueFromString(first_line, fields[1], &component_id, error)) {
1022 return false;
1023 }
jbauch083b73f2015-07-16 02:46:32 -07001024 const std::string& transport = fields[2];
Peter Boström0c4e06b2015-10-07 12:23:21 +02001025 uint32_t priority = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001026 if (!GetValueFromString(first_line, fields[3], &priority, error)) {
1027 return false;
1028 }
jbauch083b73f2015-07-16 02:46:32 -07001029 const std::string& connection_address = fields[4];
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001030 int port = 0;
1031 if (!GetValueFromString(first_line, fields[5], &port, error)) {
1032 return false;
1033 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001034 SocketAddress address(connection_address, port);
1035
1036 cricket::ProtocolType protocol;
hnsl277b2502016-12-13 05:17:23 -08001037 if (!StringToProto(transport.c_str(), &protocol) ||
1038 protocol == cricket::PROTO_TLS) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001039 return ParseFailed(first_line, "Unsupported transport type.", error);
1040 }
1041
1042 std::string candidate_type;
jbauch083b73f2015-07-16 02:46:32 -07001043 const std::string& type = fields[7];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001044 if (type == kCandidateHost) {
1045 candidate_type = cricket::LOCAL_PORT_TYPE;
1046 } else if (type == kCandidateSrflx) {
1047 candidate_type = cricket::STUN_PORT_TYPE;
1048 } else if (type == kCandidateRelay) {
1049 candidate_type = cricket::RELAY_PORT_TYPE;
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001050 } else if (type == kCandidatePrflx) {
1051 candidate_type = cricket::PRFLX_PORT_TYPE;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001052 } else {
1053 return ParseFailed(first_line, "Unsupported candidate type.", error);
1054 }
1055
1056 size_t current_position = expected_min_fields;
1057 SocketAddress related_address;
1058 // The 2 optional fields for related address
1059 // [raddr <connection-address>] [rport <port>]
1060 if (fields.size() >= (current_position + 2) &&
1061 fields[current_position] == kAttributeCandidateRaddr) {
1062 related_address.SetIP(fields[++current_position]);
1063 ++current_position;
1064 }
1065 if (fields.size() >= (current_position + 2) &&
1066 fields[current_position] == kAttributeCandidateRport) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001067 int port = 0;
1068 if (!GetValueFromString(
1069 first_line, fields[++current_position], &port, error)) {
1070 return false;
1071 }
1072 related_address.SetPort(port);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001073 ++current_position;
1074 }
1075
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001076 // If this is a TCP candidate, it has additional extension as defined in
1077 // RFC 6544.
1078 std::string tcptype;
1079 if (fields.size() >= (current_position + 2) &&
1080 fields[current_position] == kTcpCandidateType) {
1081 tcptype = fields[++current_position];
1082 ++current_position;
1083
1084 if (tcptype != cricket::TCPTYPE_ACTIVE_STR &&
1085 tcptype != cricket::TCPTYPE_PASSIVE_STR &&
1086 tcptype != cricket::TCPTYPE_SIMOPEN_STR) {
1087 return ParseFailed(first_line, "Invalid TCP candidate type.", error);
1088 }
1089
1090 if (protocol != cricket::PROTO_TCP) {
1091 return ParseFailed(first_line, "Invalid non-TCP candidate", error);
1092 }
1093 }
1094
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001095 // Extension
honghaiza54a0802015-12-16 18:37:23 -08001096 // Though non-standard, we support the ICE ufrag and pwd being signaled on
1097 // the candidate to avoid issues with confusing which generation a candidate
1098 // belongs to when trickling multiple generations at the same time.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001099 std::string username;
1100 std::string password;
Peter Boström0c4e06b2015-10-07 12:23:21 +02001101 uint32_t generation = 0;
honghaiza0c44ea2016-03-23 16:07:48 -07001102 uint16_t network_id = 0;
1103 uint16_t network_cost = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001104 for (size_t i = current_position; i + 1 < fields.size(); ++i) {
1105 // RFC 5245
1106 // *(SP extension-att-name SP extension-att-value)
1107 if (fields[i] == kAttributeCandidateGeneration) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001108 if (!GetValueFromString(first_line, fields[++i], &generation, error)) {
1109 return false;
1110 }
honghaiza54a0802015-12-16 18:37:23 -08001111 } else if (fields[i] == kAttributeCandidateUfrag) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001112 username = fields[++i];
honghaiza54a0802015-12-16 18:37:23 -08001113 } else if (fields[i] == kAttributeCandidatePwd) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001114 password = fields[++i];
honghaiza0c44ea2016-03-23 16:07:48 -07001115 } else if (fields[i] == kAttributeCandidateNetworkId) {
1116 if (!GetValueFromString(first_line, fields[++i], &network_id, error)) {
1117 return false;
1118 }
honghaize1a0c942016-02-16 14:54:56 -08001119 } else if (fields[i] == kAttributeCandidateNetworkCost) {
1120 if (!GetValueFromString(first_line, fields[++i], &network_cost, error)) {
1121 return false;
1122 }
Honghai Zhang351d77b2016-05-20 15:08:29 -07001123 network_cost = std::min(network_cost, rtc::kNetworkCostMax);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001124 } else {
1125 // Skip the unknown extension.
1126 ++i;
1127 }
1128 }
1129
guoweis@webrtc.org61c12472015-01-15 06:53:07 +00001130 *candidate = Candidate(component_id, cricket::ProtoToString(protocol),
guoweis@webrtc.org950c5182014-12-16 23:01:31 +00001131 address, priority, username, password, candidate_type,
honghaiza0c44ea2016-03-23 16:07:48 -07001132 generation, foundation, network_id, network_cost);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001133 candidate->set_related_address(related_address);
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001134 candidate->set_tcptype(tcptype);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001135 return true;
1136}
1137
1138bool ParseIceOptions(const std::string& line,
1139 std::vector<std::string>* transport_options,
1140 SdpParseError* error) {
1141 std::string ice_options;
1142 if (!GetValue(line, kAttributeIceOption, &ice_options, error)) {
1143 return false;
1144 }
1145 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001146 rtc::split(ice_options, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001147 for (size_t i = 0; i < fields.size(); ++i) {
1148 transport_options->push_back(fields[i]);
1149 }
1150 return true;
1151}
1152
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001153bool ParseSctpPort(const std::string& line,
1154 int* sctp_port,
1155 SdpParseError* error) {
1156 // draft-ietf-mmusic-sctp-sdp-07
1157 // a=sctp-port
1158 std::vector<std::string> fields;
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001159 const size_t expected_min_fields = 2;
lally69f57602015-10-08 10:15:04 -07001160 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColon, &fields);
1161 if (fields.size() < expected_min_fields) {
1162 fields.resize(0);
1163 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpace, &fields);
1164 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001165 if (fields.size() < expected_min_fields) {
1166 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1167 }
1168 if (!rtc::FromString(fields[1], sctp_port)) {
lally69f57602015-10-08 10:15:04 -07001169 return ParseFailed(line, "Invalid sctp port value.", error);
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001170 }
1171 return true;
1172}
1173
isheriff6f8d6862016-05-26 11:24:55 -07001174bool ParseExtmap(const std::string& line,
1175 RtpExtension* extmap,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001176 SdpParseError* error) {
1177 // RFC 5285
1178 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1179 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001180 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001181 kSdpDelimiterSpace, &fields);
1182 const size_t expected_min_fields = 2;
1183 if (fields.size() < expected_min_fields) {
1184 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1185 }
1186 std::string uri = fields[1];
1187
1188 std::string value_direction;
1189 if (!GetValue(fields[0], kAttributeExtmap, &value_direction, error)) {
1190 return false;
1191 }
1192 std::vector<std::string> sub_fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001193 rtc::split(value_direction, kSdpDelimiterSlash, &sub_fields);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001194 int value = 0;
1195 if (!GetValueFromString(line, sub_fields[0], &value, error)) {
1196 return false;
1197 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001198
isheriff6f8d6862016-05-26 11:24:55 -07001199 *extmap = RtpExtension(uri, value);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001200 return true;
1201}
1202
1203void BuildMediaDescription(const ContentInfo* content_info,
1204 const TransportInfo* transport_info,
1205 const MediaType media_type,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001206 const std::vector<Candidate>& candidates,
deadbeef9d3584c2016-02-16 17:54:10 -08001207 bool unified_plan_sdp,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001208 std::string* message) {
1209 ASSERT(message != NULL);
1210 if (content_info == NULL || message == NULL) {
1211 return;
1212 }
1213 // TODO: Rethink if we should use sprintfn instead of stringstream.
1214 // According to the style guide, streams should only be used for logging.
1215 // http://google-styleguide.googlecode.com/svn/
1216 // trunk/cppguide.xml?showone=Streams#Streams
1217 std::ostringstream os;
1218 const MediaContentDescription* media_desc =
henrike@webrtc.org28654cb2013-07-22 21:07:49 +00001219 static_cast<const MediaContentDescription*>(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001220 content_info->description);
1221 ASSERT(media_desc != NULL);
1222
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001223 int sctp_port = cricket::kSctpDefaultPort;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001224
1225 // RFC 4566
1226 // m=<media> <port> <proto> <fmt>
1227 // fmt is a list of payload type numbers that MAY be used in the session.
1228 const char* type = NULL;
1229 if (media_type == cricket::MEDIA_TYPE_AUDIO)
1230 type = kMediaTypeAudio;
1231 else if (media_type == cricket::MEDIA_TYPE_VIDEO)
1232 type = kMediaTypeVideo;
1233 else if (media_type == cricket::MEDIA_TYPE_DATA)
1234 type = kMediaTypeData;
1235 else
1236 ASSERT(false);
1237
1238 std::string fmt;
1239 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
1240 const VideoContentDescription* video_desc =
1241 static_cast<const VideoContentDescription*>(media_desc);
1242 for (std::vector<cricket::VideoCodec>::const_iterator it =
1243 video_desc->codecs().begin();
1244 it != video_desc->codecs().end(); ++it) {
1245 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001246 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001247 }
1248 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
1249 const AudioContentDescription* audio_desc =
1250 static_cast<const AudioContentDescription*>(media_desc);
1251 for (std::vector<cricket::AudioCodec>::const_iterator it =
1252 audio_desc->codecs().begin();
1253 it != audio_desc->codecs().end(); ++it) {
1254 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001255 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001256 }
1257 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001258 const DataContentDescription* data_desc =
1259 static_cast<const DataContentDescription*>(media_desc);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001260 if (IsDtlsSctp(media_desc->protocol())) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001261 fmt.append(" ");
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001262
1263 for (std::vector<cricket::DataCodec>::const_iterator it =
1264 data_desc->codecs().begin();
1265 it != data_desc->codecs().end(); ++it) {
solenberg9fa49752016-10-08 13:02:44 -07001266 if (cricket::CodecNamesEq(it->name, cricket::kGoogleSctpDataCodecName)
1267 && it->GetParam(cricket::kCodecParamPort, &sctp_port)) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001268 break;
1269 }
1270 }
1271
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001272 fmt.append(rtc::ToString<int>(sctp_port));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001273 } else {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001274 for (std::vector<cricket::DataCodec>::const_iterator it =
1275 data_desc->codecs().begin();
1276 it != data_desc->codecs().end(); ++it) {
1277 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001278 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001279 }
1280 }
1281 }
1282 // The fmt must never be empty. If no codecs are found, set the fmt attribute
1283 // to 0.
1284 if (fmt.empty()) {
1285 fmt = " 0";
1286 }
1287
deadbeef25ed4352016-12-12 18:37:36 -08001288 // The port number in the m line will be updated later when associated with
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001289 // the candidates.
deadbeef25ed4352016-12-12 18:37:36 -08001290 //
1291 // A port value of 0 indicates that the m= section is rejected.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001292 // RFC 3264
1293 // To reject an offered stream, the port number in the corresponding stream in
1294 // the answer MUST be set to zero.
deadbeef25ed4352016-12-12 18:37:36 -08001295 //
1296 // However, the BUNDLE draft adds a new meaning to port zero, when used along
1297 // with a=bundle-only.
1298 const std::string& port =
1299 (content_info->rejected || content_info->bundle_only) ? kMediaPortRejected
1300 : kDummyPort;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001301
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001302 rtc::SSLFingerprint* fp = (transport_info) ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001303 transport_info->description.identity_fingerprint.get() : NULL;
1304
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001305 // Add the m and c lines.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001306 InitLine(kLineTypeMedia, type, &os);
1307 os << " " << port << " " << media_desc->protocol() << fmt;
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001308 std::string mline = os.str();
1309 UpdateMediaDefaultDestination(candidates, mline, message);
1310
1311 // RFC 4566
1312 // b=AS:<bandwidth>
Peter Thatcherc0c3a862015-06-24 15:31:25 -07001313 if (media_desc->bandwidth() >= 1000) {
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001314 InitLine(kLineTypeSessionBandwidth, kApplicationSpecificMaximum, &os);
1315 os << kSdpDelimiterColon << (media_desc->bandwidth() / 1000);
1316 AddLine(os.str(), message);
1317 }
1318
deadbeef25ed4352016-12-12 18:37:36 -08001319 // Add the a=bundle-only line.
1320 if (content_info->bundle_only) {
1321 InitAttrLine(kAttributeBundleOnly, &os);
1322 AddLine(os.str(), message);
1323 }
1324
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001325 // Add the a=rtcp line.
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001326 if (IsRtp(media_desc->protocol())) {
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001327 std::string rtcp_line = GetRtcpLine(candidates);
1328 if (!rtcp_line.empty()) {
1329 AddLine(rtcp_line, message);
1330 }
1331 }
1332
honghaiza54a0802015-12-16 18:37:23 -08001333 // Build the a=candidate lines. We don't include ufrag and pwd in the
1334 // candidates in the SDP to avoid redundancy.
1335 BuildCandidate(candidates, false, message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001336
1337 // Use the transport_info to build the media level ice-ufrag and ice-pwd.
1338 if (transport_info) {
1339 // RFC 5245
1340 // ice-pwd-att = "ice-pwd" ":" password
1341 // ice-ufrag-att = "ice-ufrag" ":" ufrag
1342 // ice-ufrag
deadbeef3f7219b2015-12-28 15:17:14 -08001343 if (!transport_info->description.ice_ufrag.empty()) {
1344 InitAttrLine(kAttributeIceUfrag, &os);
1345 os << kSdpDelimiterColon << transport_info->description.ice_ufrag;
1346 AddLine(os.str(), message);
1347 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001348 // ice-pwd
deadbeef3f7219b2015-12-28 15:17:14 -08001349 if (!transport_info->description.ice_pwd.empty()) {
1350 InitAttrLine(kAttributeIcePwd, &os);
1351 os << kSdpDelimiterColon << transport_info->description.ice_pwd;
1352 AddLine(os.str(), message);
1353 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001354
1355 // draft-petithuguenin-mmusic-ice-attributes-level-03
1356 BuildIceOptions(transport_info->description.transport_options, message);
1357
1358 // RFC 4572
1359 // fingerprint-attribute =
1360 // "fingerprint" ":" hash-func SP fingerprint
1361 if (fp) {
1362 // Insert the fingerprint attribute.
1363 InitAttrLine(kAttributeFingerprint, &os);
1364 os << kSdpDelimiterColon
1365 << fp->algorithm << kSdpDelimiterSpace
1366 << fp->GetRfc4572Fingerprint();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001367 AddLine(os.str(), message);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001368
1369 // Inserting setup attribute.
1370 if (transport_info->description.connection_role !=
1371 cricket::CONNECTIONROLE_NONE) {
1372 // Making sure we are not using "passive" mode.
1373 cricket::ConnectionRole role =
1374 transport_info->description.connection_role;
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001375 std::string dtls_role_str;
1376 VERIFY(cricket::ConnectionRoleToString(role, &dtls_role_str));
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001377 InitAttrLine(kAttributeSetup, &os);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001378 os << kSdpDelimiterColon << dtls_role_str;
1379 AddLine(os.str(), message);
1380 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001381 }
1382 }
1383
1384 // RFC 3388
1385 // mid-attribute = "a=mid:" identification-tag
1386 // identification-tag = token
1387 // Use the content name as the mid identification-tag.
1388 InitAttrLine(kAttributeMid, &os);
1389 os << kSdpDelimiterColon << content_info->name;
1390 AddLine(os.str(), message);
1391
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001392 if (IsDtlsSctp(media_desc->protocol())) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001393 BuildSctpContentAttributes(message, sctp_port);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001394 } else if (IsRtp(media_desc->protocol())) {
deadbeef9d3584c2016-02-16 17:54:10 -08001395 BuildRtpContentAttributes(media_desc, media_type, unified_plan_sdp,
1396 message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001397 }
1398}
1399
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001400void BuildSctpContentAttributes(std::string* message, int sctp_port) {
wu@webrtc.org78187522013-10-07 23:32:02 +00001401 // draft-ietf-mmusic-sctp-sdp-04
1402 // a=sctpmap:sctpmap-number protocol [streams]
lally69f57602015-10-08 10:15:04 -07001403 // TODO(lally): switch this over to mmusic-sctp-sdp-12 (or later), with
1404 // 'a=sctp-port:'
wu@webrtc.org78187522013-10-07 23:32:02 +00001405 std::ostringstream os;
1406 InitAttrLine(kAttributeSctpmap, &os);
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001407 os << kSdpDelimiterColon << sctp_port << kSdpDelimiterSpace
wu@webrtc.org78187522013-10-07 23:32:02 +00001408 << kDefaultSctpmapProtocol << kSdpDelimiterSpace
Taylor Brandstetter1d7a6372016-08-24 13:15:27 -07001409 << cricket::kMaxSctpStreams;
wu@webrtc.org78187522013-10-07 23:32:02 +00001410 AddLine(os.str(), message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001411}
1412
deadbeef9d3584c2016-02-16 17:54:10 -08001413// If unified_plan_sdp is true, will use "a=msid".
1414void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
1415 const MediaType media_type,
1416 bool unified_plan_sdp,
1417 std::string* message) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001418 std::ostringstream os;
1419 // RFC 5285
1420 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1421 // The definitions MUST be either all session level or all media level. This
1422 // implementation uses all media level.
1423 for (size_t i = 0; i < media_desc->rtp_header_extensions().size(); ++i) {
1424 InitAttrLine(kAttributeExtmap, &os);
1425 os << kSdpDelimiterColon << media_desc->rtp_header_extensions()[i].id
1426 << kSdpDelimiterSpace << media_desc->rtp_header_extensions()[i].uri;
1427 AddLine(os.str(), message);
1428 }
1429
1430 // RFC 3264
1431 // a=sendrecv || a=sendonly || a=sendrecv || a=inactive
deadbeefc80741f2015-10-22 13:14:45 -07001432 switch (media_desc->direction()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001433 case cricket::MD_INACTIVE:
1434 InitAttrLine(kAttributeInactive, &os);
1435 break;
1436 case cricket::MD_SENDONLY:
1437 InitAttrLine(kAttributeSendOnly, &os);
1438 break;
1439 case cricket::MD_RECVONLY:
1440 InitAttrLine(kAttributeRecvOnly, &os);
1441 break;
1442 case cricket::MD_SENDRECV:
1443 default:
1444 InitAttrLine(kAttributeSendRecv, &os);
1445 break;
1446 }
1447 AddLine(os.str(), message);
1448
deadbeef9d3584c2016-02-16 17:54:10 -08001449 // draft-ietf-mmusic-msid-11
1450 // a=msid:<stream id> <track id>
1451 if (unified_plan_sdp && !media_desc->streams().empty()) {
1452 if (media_desc->streams().size() > 1u) {
1453 LOG(LS_WARNING) << "Trying to serialize unified plan SDP with more than "
1454 << "one track in a media section. Omitting 'a=msid'.";
1455 } else {
1456 auto track = media_desc->streams().begin();
1457 const std::string& stream_id = track->sync_label;
deadbeef9d3584c2016-02-16 17:54:10 -08001458 InitAttrLine(kAttributeMsid, &os);
1459 os << kSdpDelimiterColon << stream_id << kSdpDelimiterSpace << track->id;
1460 AddLine(os.str(), message);
1461 }
1462 }
1463
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001464 // RFC 5761
1465 // a=rtcp-mux
1466 if (media_desc->rtcp_mux()) {
1467 InitAttrLine(kAttributeRtcpMux, &os);
1468 AddLine(os.str(), message);
1469 }
1470
deadbeef13871492015-12-09 12:37:51 -08001471 // RFC 5506
1472 // a=rtcp-rsize
1473 if (media_desc->rtcp_reduced_size()) {
1474 InitAttrLine(kAttributeRtcpReducedSize, &os);
1475 AddLine(os.str(), message);
1476 }
1477
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001478 // RFC 4568
1479 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
1480 for (std::vector<CryptoParams>::const_iterator it =
1481 media_desc->cryptos().begin();
1482 it != media_desc->cryptos().end(); ++it) {
1483 InitAttrLine(kAttributeCrypto, &os);
1484 os << kSdpDelimiterColon << it->tag << " " << it->cipher_suite << " "
1485 << it->key_params;
1486 if (!it->session_params.empty()) {
1487 os << " " << it->session_params;
1488 }
1489 AddLine(os.str(), message);
1490 }
1491
1492 // RFC 4566
1493 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1494 // [/<encodingparameters>]
1495 BuildRtpMap(media_desc, media_type, message);
1496
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001497 for (StreamParamsVec::const_iterator track = media_desc->streams().begin();
1498 track != media_desc->streams().end(); ++track) {
1499 // Require that the track belongs to a media stream,
1500 // ie the sync_label is set. This extra check is necessary since the
1501 // MediaContentDescription always contains a streamparam with an ssrc even
1502 // if no track or media stream have been created.
1503 if (track->sync_label.empty()) continue;
1504
1505 // Build the ssrc-group lines.
1506 for (size_t i = 0; i < track->ssrc_groups.size(); ++i) {
1507 // RFC 5576
1508 // a=ssrc-group:<semantics> <ssrc-id> ...
1509 if (track->ssrc_groups[i].ssrcs.empty()) {
1510 continue;
1511 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001512 InitAttrLine(kAttributeSsrcGroup, &os);
1513 os << kSdpDelimiterColon << track->ssrc_groups[i].semantics;
Peter Boström0c4e06b2015-10-07 12:23:21 +02001514 std::vector<uint32_t>::const_iterator ssrc =
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001515 track->ssrc_groups[i].ssrcs.begin();
1516 for (; ssrc != track->ssrc_groups[i].ssrcs.end(); ++ssrc) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02001517 os << kSdpDelimiterSpace << rtc::ToString<uint32_t>(*ssrc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001518 }
1519 AddLine(os.str(), message);
1520 }
1521 // Build the ssrc lines for each ssrc.
1522 for (size_t i = 0; i < track->ssrcs.size(); ++i) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02001523 uint32_t ssrc = track->ssrcs[i];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001524 // RFC 5576
1525 // a=ssrc:<ssrc-id> cname:<value>
1526 AddSsrcLine(ssrc, kSsrcAttributeCname,
1527 track->cname, message);
1528
1529 // draft-alvestrand-mmusic-msid-00
1530 // a=ssrc:<ssrc-id> msid:identifier [appdata]
deadbeef9d3584c2016-02-16 17:54:10 -08001531 // The appdata consists of the "id" attribute of a MediaStreamTrack,
1532 // which corresponds to the "id" attribute of StreamParams.
1533 const std::string& stream_id = track->sync_label;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001534 InitAttrLine(kAttributeSsrc, &os);
1535 os << kSdpDelimiterColon << ssrc << kSdpDelimiterSpace
deadbeef9d3584c2016-02-16 17:54:10 -08001536 << kSsrcAttributeMsid << kSdpDelimiterColon << stream_id
1537 << kSdpDelimiterSpace << track->id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001538 AddLine(os.str(), message);
1539
deadbeef9d3584c2016-02-16 17:54:10 -08001540 // TODO(ronghuawu): Remove below code which is for backward
1541 // compatibility.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001542 // draft-alvestrand-rtcweb-mid-01
1543 // a=ssrc:<ssrc-id> mslabel:<value>
1544 // The label isn't yet defined.
1545 // a=ssrc:<ssrc-id> label:<value>
1546 AddSsrcLine(ssrc, kSsrcAttributeMslabel, track->sync_label, message);
1547 AddSsrcLine(ssrc, kSSrcAttributeLabel, track->id, message);
1548 }
1549 }
1550}
1551
1552void WriteFmtpHeader(int payload_type, std::ostringstream* os) {
1553 // fmtp header: a=fmtp:|payload_type| <parameters>
1554 // Add a=fmtp
1555 InitAttrLine(kAttributeFmtp, os);
1556 // Add :|payload_type|
1557 *os << kSdpDelimiterColon << payload_type;
1558}
1559
1560void WriteRtcpFbHeader(int payload_type, std::ostringstream* os) {
1561 // rtcp-fb header: a=rtcp-fb:|payload_type|
1562 // <parameters>/<ccm <ccm_parameters>>
1563 // Add a=rtcp-fb
1564 InitAttrLine(kAttributeRtcpFb, os);
1565 // Add :
1566 *os << kSdpDelimiterColon;
1567 if (payload_type == kWildcardPayloadType) {
1568 *os << "*";
1569 } else {
1570 *os << payload_type;
1571 }
1572}
1573
1574void WriteFmtpParameter(const std::string& parameter_name,
1575 const std::string& parameter_value,
1576 std::ostringstream* os) {
1577 // fmtp parameters: |parameter_name|=|parameter_value|
1578 *os << parameter_name << kSdpDelimiterEqual << parameter_value;
1579}
1580
1581void WriteFmtpParameters(const cricket::CodecParameterMap& parameters,
1582 std::ostringstream* os) {
1583 for (cricket::CodecParameterMap::const_iterator fmtp = parameters.begin();
1584 fmtp != parameters.end(); ++fmtp) {
hta62a216e2016-04-15 11:02:14 -07001585 // Parameters are a semicolon-separated list, no spaces.
1586 // The list is separated from the header by a space.
1587 if (fmtp == parameters.begin()) {
1588 *os << kSdpDelimiterSpace;
1589 } else {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001590 *os << kSdpDelimiterSemicolon;
1591 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001592 WriteFmtpParameter(fmtp->first, fmtp->second, os);
1593 }
1594}
1595
1596bool IsFmtpParam(const std::string& name) {
1597 const char* kFmtpParams[] = {
htaa6b99442016-04-12 10:29:17 -07001598 // TODO(hta): Split FMTP parameters apart from parameters in general.
1599 // FMTP parameters are codec specific, not generic.
1600 kCodecParamMinPTime,
1601 kCodecParamSPropStereo,
1602 kCodecParamStereo,
1603 kCodecParamUseInbandFec,
1604 kCodecParamUseDtx,
1605 kCodecParamStartBitrate,
1606 kCodecParamMaxBitrate,
1607 kCodecParamMinBitrate,
1608 kCodecParamMaxQuantization,
1609 kCodecParamSctpProtocol,
1610 kCodecParamSctpStreams,
1611 kCodecParamMaxAverageBitrate,
1612 kCodecParamMaxPlaybackRate,
1613 kCodecParamAssociatedPayloadType,
1614 cricket::kH264FmtpPacketizationMode,
1615 cricket::kH264FmtpLevelAsymmetryAllowed,
brandtr87d7d772016-11-07 03:03:41 -08001616 cricket::kH264FmtpProfileLevelId,
1617 cricket::kFlexfecFmtpRepairWindow};
tfarina5237aaf2015-11-10 23:44:30 -08001618 for (size_t i = 0; i < arraysize(kFmtpParams); ++i) {
htaa6b99442016-04-12 10:29:17 -07001619 if (name.compare(kFmtpParams[i]) == 0) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001620 return true;
1621 }
1622 }
1623 return false;
1624}
1625
1626// Retreives fmtp parameters from |params|, which may contain other parameters
1627// as well, and puts them in |fmtp_parameters|.
1628void GetFmtpParams(const cricket::CodecParameterMap& params,
1629 cricket::CodecParameterMap* fmtp_parameters) {
1630 for (cricket::CodecParameterMap::const_iterator iter = params.begin();
1631 iter != params.end(); ++iter) {
1632 if (IsFmtpParam(iter->first)) {
1633 (*fmtp_parameters)[iter->first] = iter->second;
1634 }
1635 }
1636}
1637
1638template <class T>
1639void AddFmtpLine(const T& codec, std::string* message) {
1640 cricket::CodecParameterMap fmtp_parameters;
1641 GetFmtpParams(codec.params, &fmtp_parameters);
1642 if (fmtp_parameters.empty()) {
1643 // No need to add an fmtp if it will have no (optional) parameters.
1644 return;
1645 }
1646 std::ostringstream os;
1647 WriteFmtpHeader(codec.id, &os);
1648 WriteFmtpParameters(fmtp_parameters, &os);
1649 AddLine(os.str(), message);
1650 return;
1651}
1652
1653template <class T>
1654void AddRtcpFbLines(const T& codec, std::string* message) {
1655 for (std::vector<cricket::FeedbackParam>::const_iterator iter =
1656 codec.feedback_params.params().begin();
1657 iter != codec.feedback_params.params().end(); ++iter) {
1658 std::ostringstream os;
1659 WriteRtcpFbHeader(codec.id, &os);
1660 os << " " << iter->id();
1661 if (!iter->param().empty()) {
1662 os << " " << iter->param();
1663 }
1664 AddLine(os.str(), message);
1665 }
1666}
1667
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001668bool AddSctpDataCodec(DataContentDescription* media_desc,
1669 int sctp_port) {
solenberg9fa49752016-10-08 13:02:44 -07001670 for (const auto& codec : media_desc->codecs()) {
1671 if (cricket::CodecNamesEq(codec.name, cricket::kGoogleSctpDataCodecName)) {
1672 return ParseFailed("",
1673 "Can't have multiple sctp port attributes.",
1674 NULL);
1675 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001676 }
1677 // Add the SCTP Port number as a pseudo-codec "port" parameter
solenberg9fa49752016-10-08 13:02:44 -07001678 cricket::DataCodec codec_port(cricket::kGoogleSctpDataCodecPlType,
deadbeef67cf2c12016-04-13 10:07:16 -07001679 cricket::kGoogleSctpDataCodecName);
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001680 codec_port.SetParam(cricket::kCodecParamPort, sctp_port);
1681 LOG(INFO) << "AddSctpDataCodec: Got SCTP Port Number "
1682 << sctp_port;
1683 media_desc->AddCodec(codec_port);
1684 return true;
1685}
1686
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001687bool GetMinValue(const std::vector<int>& values, int* value) {
1688 if (values.empty()) {
1689 return false;
1690 }
1691 std::vector<int>::const_iterator found =
1692 std::min_element(values.begin(), values.end());
1693 *value = *found;
1694 return true;
1695}
1696
1697bool GetParameter(const std::string& name,
1698 const cricket::CodecParameterMap& params, int* value) {
1699 std::map<std::string, std::string>::const_iterator found =
1700 params.find(name);
1701 if (found == params.end()) {
1702 return false;
1703 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001704 if (!rtc::FromString(found->second, value)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001705 return false;
1706 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001707 return true;
1708}
1709
1710void BuildRtpMap(const MediaContentDescription* media_desc,
1711 const MediaType media_type,
1712 std::string* message) {
1713 ASSERT(message != NULL);
1714 ASSERT(media_desc != NULL);
1715 std::ostringstream os;
1716 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
1717 const VideoContentDescription* video_desc =
1718 static_cast<const VideoContentDescription*>(media_desc);
1719 for (std::vector<cricket::VideoCodec>::const_iterator it =
1720 video_desc->codecs().begin();
1721 it != video_desc->codecs().end(); ++it) {
1722 // RFC 4566
1723 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1724 // [/<encodingparameters>]
1725 if (it->id != kWildcardPayloadType) {
1726 InitAttrLine(kAttributeRtpmap, &os);
1727 os << kSdpDelimiterColon << it->id << " " << it->name
1728 << "/" << kDefaultVideoClockrate;
1729 AddLine(os.str(), message);
1730 }
1731 AddRtcpFbLines(*it, message);
1732 AddFmtpLine(*it, message);
1733 }
1734 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
1735 const AudioContentDescription* audio_desc =
1736 static_cast<const AudioContentDescription*>(media_desc);
1737 std::vector<int> ptimes;
1738 std::vector<int> maxptimes;
1739 int max_minptime = 0;
1740 for (std::vector<cricket::AudioCodec>::const_iterator it =
1741 audio_desc->codecs().begin();
1742 it != audio_desc->codecs().end(); ++it) {
1743 ASSERT(!it->name.empty());
1744 // RFC 4566
1745 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1746 // [/<encodingparameters>]
1747 InitAttrLine(kAttributeRtpmap, &os);
1748 os << kSdpDelimiterColon << it->id << " ";
1749 os << it->name << "/" << it->clockrate;
1750 if (it->channels != 1) {
1751 os << "/" << it->channels;
1752 }
1753 AddLine(os.str(), message);
1754 AddRtcpFbLines(*it, message);
1755 AddFmtpLine(*it, message);
1756 int minptime = 0;
1757 if (GetParameter(kCodecParamMinPTime, it->params, &minptime)) {
1758 max_minptime = std::max(minptime, max_minptime);
1759 }
1760 int ptime;
1761 if (GetParameter(kCodecParamPTime, it->params, &ptime)) {
1762 ptimes.push_back(ptime);
1763 }
1764 int maxptime;
1765 if (GetParameter(kCodecParamMaxPTime, it->params, &maxptime)) {
1766 maxptimes.push_back(maxptime);
1767 }
1768 }
1769 // Populate the maxptime attribute with the smallest maxptime of all codecs
1770 // under the same m-line.
1771 int min_maxptime = INT_MAX;
1772 if (GetMinValue(maxptimes, &min_maxptime)) {
1773 AddAttributeLine(kCodecParamMaxPTime, min_maxptime, message);
1774 }
1775 ASSERT(min_maxptime > max_minptime);
1776 // Populate the ptime attribute with the smallest ptime or the largest
1777 // minptime, whichever is the largest, for all codecs under the same m-line.
1778 int ptime = INT_MAX;
1779 if (GetMinValue(ptimes, &ptime)) {
1780 ptime = std::min(ptime, min_maxptime);
1781 ptime = std::max(ptime, max_minptime);
1782 AddAttributeLine(kCodecParamPTime, ptime, message);
1783 }
1784 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
1785 const DataContentDescription* data_desc =
1786 static_cast<const DataContentDescription*>(media_desc);
1787 for (std::vector<cricket::DataCodec>::const_iterator it =
1788 data_desc->codecs().begin();
1789 it != data_desc->codecs().end(); ++it) {
1790 // RFC 4566
1791 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1792 // [/<encodingparameters>]
1793 InitAttrLine(kAttributeRtpmap, &os);
1794 os << kSdpDelimiterColon << it->id << " "
1795 << it->name << "/" << it->clockrate;
1796 AddLine(os.str(), message);
1797 }
1798 }
1799}
1800
1801void BuildCandidate(const std::vector<Candidate>& candidates,
honghaiza54a0802015-12-16 18:37:23 -08001802 bool include_ufrag,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001803 std::string* message) {
1804 std::ostringstream os;
1805
1806 for (std::vector<Candidate>::const_iterator it = candidates.begin();
1807 it != candidates.end(); ++it) {
1808 // RFC 5245
1809 // a=candidate:<foundation> <component-id> <transport> <priority>
1810 // <connection-address> <port> typ <candidate-types>
1811 // [raddr <connection-address>] [rport <port>]
1812 // *(SP extension-att-name SP extension-att-value)
1813 std::string type;
1814 // Map the cricket candidate type to "host" / "srflx" / "prflx" / "relay"
1815 if (it->type() == cricket::LOCAL_PORT_TYPE) {
1816 type = kCandidateHost;
1817 } else if (it->type() == cricket::STUN_PORT_TYPE) {
1818 type = kCandidateSrflx;
1819 } else if (it->type() == cricket::RELAY_PORT_TYPE) {
1820 type = kCandidateRelay;
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001821 } else if (it->type() == cricket::PRFLX_PORT_TYPE) {
1822 type = kCandidatePrflx;
1823 // Peer reflexive candidate may be signaled for being removed.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001824 } else {
1825 ASSERT(false);
Peter Thatcher019087f2015-04-28 09:06:26 -07001826 // Never write out candidates if we don't know the type.
1827 continue;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001828 }
1829
1830 InitAttrLine(kAttributeCandidate, &os);
1831 os << kSdpDelimiterColon
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001832 << it->foundation() << " "
1833 << it->component() << " "
1834 << it->protocol() << " "
1835 << it->priority() << " "
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001836 << it->address().ipaddr().ToString() << " "
1837 << it->address().PortAsString() << " "
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001838 << kAttributeCandidateTyp << " "
1839 << type << " ";
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001840
1841 // Related address
1842 if (!it->related_address().IsNil()) {
1843 os << kAttributeCandidateRaddr << " "
1844 << it->related_address().ipaddr().ToString() << " "
1845 << kAttributeCandidateRport << " "
1846 << it->related_address().PortAsString() << " ";
1847 }
1848
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001849 if (it->protocol() == cricket::TCP_PROTOCOL_NAME) {
mallinath@webrtc.orge999bd02014-08-13 06:05:55 +00001850 os << kTcpCandidateType << " " << it->tcptype() << " ";
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001851 }
1852
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001853 // Extensions
1854 os << kAttributeCandidateGeneration << " " << it->generation();
honghaiza54a0802015-12-16 18:37:23 -08001855 if (include_ufrag && !it->username().empty()) {
1856 os << " " << kAttributeCandidateUfrag << " " << it->username();
1857 }
honghaiza0c44ea2016-03-23 16:07:48 -07001858 if (it->network_id() > 0) {
1859 os << " " << kAttributeCandidateNetworkId << " " << it->network_id();
1860 }
honghaize1a0c942016-02-16 14:54:56 -08001861 if (it->network_cost() > 0) {
1862 os << " " << kAttributeCandidateNetworkCost << " " << it->network_cost();
1863 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001864
1865 AddLine(os.str(), message);
1866 }
1867}
1868
1869void BuildIceOptions(const std::vector<std::string>& transport_options,
1870 std::string* message) {
1871 if (!transport_options.empty()) {
1872 std::ostringstream os;
1873 InitAttrLine(kAttributeIceOption, &os);
1874 os << kSdpDelimiterColon << transport_options[0];
1875 for (size_t i = 1; i < transport_options.size(); ++i) {
1876 os << kSdpDelimiterSpace << transport_options[i];
1877 }
1878 AddLine(os.str(), message);
1879 }
1880}
1881
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001882bool IsRtp(const std::string& protocol) {
1883 return protocol.empty() ||
1884 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
1885}
1886
1887bool IsDtlsSctp(const std::string& protocol) {
1888 // This intentionally excludes "SCTP" and "SCTP/DTLS".
lally@webrtc.orga7470932015-02-24 20:19:21 +00001889 return protocol.find(cricket::kMediaProtocolDtlsSctp) != std::string::npos;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001890}
1891
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001892bool ParseSessionDescription(const std::string& message, size_t* pos,
1893 std::string* session_id,
1894 std::string* session_version,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001895 TransportDescription* session_td,
1896 RtpHeaderExtensions* session_extmaps,
1897 cricket::SessionDescription* desc,
1898 SdpParseError* error) {
1899 std::string line;
1900
deadbeefc80741f2015-10-22 13:14:45 -07001901 desc->set_msid_supported(false);
1902
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001903 // RFC 4566
1904 // v= (protocol version)
1905 if (!GetLineWithType(message, pos, &line, kLineTypeVersion)) {
1906 return ParseFailedExpectLine(message, *pos, kLineTypeVersion,
1907 std::string(), error);
1908 }
1909 // RFC 4566
1910 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
1911 // <unicast-address>
1912 if (!GetLineWithType(message, pos, &line, kLineTypeOrigin)) {
1913 return ParseFailedExpectLine(message, *pos, kLineTypeOrigin,
1914 std::string(), error);
1915 }
1916 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001917 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001918 kSdpDelimiterSpace, &fields);
1919 const size_t expected_fields = 6;
1920 if (fields.size() != expected_fields) {
1921 return ParseFailedExpectFieldNum(line, expected_fields, error);
1922 }
1923 *session_id = fields[1];
1924 *session_version = fields[2];
1925
1926 // RFC 4566
1927 // s= (session name)
1928 if (!GetLineWithType(message, pos, &line, kLineTypeSessionName)) {
1929 return ParseFailedExpectLine(message, *pos, kLineTypeSessionName,
1930 std::string(), error);
1931 }
1932
1933 // Optional lines
1934 // Those are the optional lines, so shouldn't return false if not present.
1935 // RFC 4566
1936 // i=* (session information)
1937 GetLineWithType(message, pos, &line, kLineTypeSessionInfo);
1938
1939 // RFC 4566
1940 // u=* (URI of description)
1941 GetLineWithType(message, pos, &line, kLineTypeSessionUri);
1942
1943 // RFC 4566
1944 // e=* (email address)
1945 GetLineWithType(message, pos, &line, kLineTypeSessionEmail);
1946
1947 // RFC 4566
1948 // p=* (phone number)
1949 GetLineWithType(message, pos, &line, kLineTypeSessionPhone);
1950
1951 // RFC 4566
1952 // c=* (connection information -- not required if included in
1953 // all media)
1954 GetLineWithType(message, pos, &line, kLineTypeConnection);
1955
1956 // RFC 4566
1957 // b=* (zero or more bandwidth information lines)
1958 while (GetLineWithType(message, pos, &line, kLineTypeSessionBandwidth)) {
1959 // By pass zero or more b lines.
1960 }
1961
1962 // RFC 4566
1963 // One or more time descriptions ("t=" and "r=" lines; see below)
1964 // t= (time the session is active)
1965 // r=* (zero or more repeat times)
1966 // Ensure there's at least one time description
1967 if (!GetLineWithType(message, pos, &line, kLineTypeTiming)) {
1968 return ParseFailedExpectLine(message, *pos, kLineTypeTiming, std::string(),
1969 error);
1970 }
1971
1972 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
1973 // By pass zero or more r lines.
1974 }
1975
1976 // Go through the rest of the time descriptions
1977 while (GetLineWithType(message, pos, &line, kLineTypeTiming)) {
1978 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
1979 // By pass zero or more r lines.
1980 }
1981 }
1982
1983 // RFC 4566
1984 // z=* (time zone adjustments)
1985 GetLineWithType(message, pos, &line, kLineTypeTimeZone);
1986
1987 // RFC 4566
1988 // k=* (encryption key)
1989 GetLineWithType(message, pos, &line, kLineTypeEncryptionKey);
1990
1991 // RFC 4566
1992 // a=* (zero or more session attribute lines)
1993 while (GetLineWithType(message, pos, &line, kLineTypeAttributes)) {
1994 if (HasAttribute(line, kAttributeGroup)) {
1995 if (!ParseGroupAttribute(line, desc, error)) {
1996 return false;
1997 }
1998 } else if (HasAttribute(line, kAttributeIceUfrag)) {
1999 if (!GetValue(line, kAttributeIceUfrag,
2000 &(session_td->ice_ufrag), error)) {
2001 return false;
2002 }
2003 } else if (HasAttribute(line, kAttributeIcePwd)) {
2004 if (!GetValue(line, kAttributeIcePwd, &(session_td->ice_pwd), error)) {
2005 return false;
2006 }
2007 } else if (HasAttribute(line, kAttributeIceLite)) {
2008 session_td->ice_mode = cricket::ICEMODE_LITE;
2009 } else if (HasAttribute(line, kAttributeIceOption)) {
2010 if (!ParseIceOptions(line, &(session_td->transport_options), error)) {
2011 return false;
2012 }
2013 } else if (HasAttribute(line, kAttributeFingerprint)) {
2014 if (session_td->identity_fingerprint.get()) {
2015 return ParseFailed(
2016 line,
2017 "Can't have multiple fingerprint attributes at the same level.",
2018 error);
2019 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002020 rtc::SSLFingerprint* fingerprint = NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002021 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
2022 return false;
2023 }
2024 session_td->identity_fingerprint.reset(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002025 } else if (HasAttribute(line, kAttributeSetup)) {
2026 if (!ParseDtlsSetup(line, &(session_td->connection_role), error)) {
2027 return false;
2028 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002029 } else if (HasAttribute(line, kAttributeMsidSemantics)) {
2030 std::string semantics;
2031 if (!GetValue(line, kAttributeMsidSemantics, &semantics, error)) {
2032 return false;
2033 }
deadbeefc80741f2015-10-22 13:14:45 -07002034 desc->set_msid_supported(
2035 CaseInsensitiveFind(semantics, kMediaStreamSemantic));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002036 } else if (HasAttribute(line, kAttributeExtmap)) {
isheriff6f8d6862016-05-26 11:24:55 -07002037 RtpExtension extmap;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002038 if (!ParseExtmap(line, &extmap, error)) {
2039 return false;
2040 }
2041 session_extmaps->push_back(extmap);
2042 }
2043 }
2044
2045 return true;
2046}
2047
2048bool ParseGroupAttribute(const std::string& line,
2049 cricket::SessionDescription* desc,
2050 SdpParseError* error) {
2051 ASSERT(desc != NULL);
2052
2053 // RFC 5888 and draft-holmberg-mmusic-sdp-bundle-negotiation-00
2054 // a=group:BUNDLE video voice
2055 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002056 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002057 kSdpDelimiterSpace, &fields);
2058 std::string semantics;
2059 if (!GetValue(fields[0], kAttributeGroup, &semantics, error)) {
2060 return false;
2061 }
2062 cricket::ContentGroup group(semantics);
2063 for (size_t i = 1; i < fields.size(); ++i) {
2064 group.AddContentName(fields[i]);
2065 }
2066 desc->AddGroup(group);
2067 return true;
2068}
2069
2070static bool ParseFingerprintAttribute(const std::string& line,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002071 rtc::SSLFingerprint** fingerprint,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002072 SdpParseError* error) {
2073 if (!IsLineType(line, kLineTypeAttributes) ||
2074 !HasAttribute(line, kAttributeFingerprint)) {
2075 return ParseFailedExpectLine(line, 0, kLineTypeAttributes,
2076 kAttributeFingerprint, error);
2077 }
2078
2079 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002080 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002081 kSdpDelimiterSpace, &fields);
2082 const size_t expected_fields = 2;
2083 if (fields.size() != expected_fields) {
2084 return ParseFailedExpectFieldNum(line, expected_fields, error);
2085 }
2086
2087 // The first field here is "fingerprint:<hash>.
2088 std::string algorithm;
2089 if (!GetValue(fields[0], kAttributeFingerprint, &algorithm, error)) {
2090 return false;
2091 }
2092
2093 // Downcase the algorithm. Note that we don't need to downcase the
2094 // fingerprint because hex_decode can handle upper-case.
2095 std::transform(algorithm.begin(), algorithm.end(), algorithm.begin(),
2096 ::tolower);
2097
2098 // The second field is the digest value. De-hexify it.
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002099 *fingerprint = rtc::SSLFingerprint::CreateFromRfc4572(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002100 algorithm, fields[1]);
2101 if (!*fingerprint) {
2102 return ParseFailed(line,
2103 "Failed to create fingerprint from the digest.",
2104 error);
2105 }
2106
2107 return true;
2108}
2109
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002110static bool ParseDtlsSetup(const std::string& line,
2111 cricket::ConnectionRole* role,
2112 SdpParseError* error) {
2113 // setup-attr = "a=setup:" role
2114 // role = "active" / "passive" / "actpass" / "holdconn"
2115 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002116 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColon, &fields);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002117 const size_t expected_fields = 2;
2118 if (fields.size() != expected_fields) {
2119 return ParseFailedExpectFieldNum(line, expected_fields, error);
2120 }
2121 std::string role_str = fields[1];
2122 if (!cricket::StringToConnectionRole(role_str, role)) {
2123 return ParseFailed(line, "Invalid attribute value.", error);
2124 }
2125 return true;
2126}
2127
deadbeef9d3584c2016-02-16 17:54:10 -08002128static bool ParseMsidAttribute(const std::string& line,
2129 std::string* stream_id,
2130 std::string* track_id,
2131 SdpParseError* error) {
2132 // draft-ietf-mmusic-msid-11
2133 // a=msid:<stream id> <track id>
2134 // msid-value = msid-id [ SP msid-appdata ]
2135 // msid-id = 1*64token-char ; see RFC 4566
2136 // msid-appdata = 1*64token-char ; see RFC 4566
2137 std::string field1;
2138 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
2139 &field1, track_id)) {
2140 const size_t expected_fields = 2;
2141 return ParseFailedExpectFieldNum(line, expected_fields, error);
2142 }
2143
2144 // msid:<msid-id>
2145 if (!GetValue(field1, kAttributeMsid, stream_id, error)) {
2146 return false;
2147 }
2148 return true;
2149}
2150
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002151// RFC 3551
2152// PT encoding media type clock rate channels
2153// name (Hz)
2154// 0 PCMU A 8,000 1
2155// 1 reserved A
2156// 2 reserved A
2157// 3 GSM A 8,000 1
2158// 4 G723 A 8,000 1
2159// 5 DVI4 A 8,000 1
2160// 6 DVI4 A 16,000 1
2161// 7 LPC A 8,000 1
2162// 8 PCMA A 8,000 1
2163// 9 G722 A 8,000 1
2164// 10 L16 A 44,100 2
2165// 11 L16 A 44,100 1
2166// 12 QCELP A 8,000 1
2167// 13 CN A 8,000 1
2168// 14 MPA A 90,000 (see text)
2169// 15 G728 A 8,000 1
2170// 16 DVI4 A 11,025 1
2171// 17 DVI4 A 22,050 1
2172// 18 G729 A 8,000 1
2173struct StaticPayloadAudioCodec {
2174 const char* name;
2175 int clockrate;
Peter Kasting69558702016-01-12 16:26:35 -08002176 size_t channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002177};
2178static const StaticPayloadAudioCodec kStaticPayloadAudioCodecs[] = {
2179 { "PCMU", 8000, 1 },
2180 { "reserved", 0, 0 },
2181 { "reserved", 0, 0 },
2182 { "GSM", 8000, 1 },
2183 { "G723", 8000, 1 },
2184 { "DVI4", 8000, 1 },
2185 { "DVI4", 16000, 1 },
2186 { "LPC", 8000, 1 },
2187 { "PCMA", 8000, 1 },
2188 { "G722", 8000, 1 },
2189 { "L16", 44100, 2 },
2190 { "L16", 44100, 1 },
2191 { "QCELP", 8000, 1 },
2192 { "CN", 8000, 1 },
2193 { "MPA", 90000, 1 },
2194 { "G728", 8000, 1 },
2195 { "DVI4", 11025, 1 },
2196 { "DVI4", 22050, 1 },
2197 { "G729", 8000, 1 },
2198};
2199
2200void MaybeCreateStaticPayloadAudioCodecs(
2201 const std::vector<int>& fmts, AudioContentDescription* media_desc) {
2202 if (!media_desc) {
2203 return;
2204 }
deadbeef67cf2c12016-04-13 10:07:16 -07002205 RTC_DCHECK(media_desc->codecs().empty());
solenberg9fa49752016-10-08 13:02:44 -07002206 for (int payload_type : fmts) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002207 if (!media_desc->HasCodec(payload_type) &&
2208 payload_type >= 0 &&
kjellander3e33bfe2016-06-20 07:04:09 -07002209 static_cast<uint32_t>(payload_type) <
2210 arraysize(kStaticPayloadAudioCodecs)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002211 std::string encoding_name = kStaticPayloadAudioCodecs[payload_type].name;
2212 int clock_rate = kStaticPayloadAudioCodecs[payload_type].clockrate;
Peter Kasting69558702016-01-12 16:26:35 -08002213 size_t channels = kStaticPayloadAudioCodecs[payload_type].channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002214 media_desc->AddCodec(cricket::AudioCodec(payload_type, encoding_name,
deadbeef67cf2c12016-04-13 10:07:16 -07002215 clock_rate, 0, channels));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002216 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002217 }
2218}
2219
2220template <class C>
2221static C* ParseContentDescription(const std::string& message,
2222 const MediaType media_type,
2223 int mline_index,
2224 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -07002225 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002226 size_t* pos,
2227 std::string* content_name,
deadbeef25ed4352016-12-12 18:37:36 -08002228 bool* bundle_only,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002229 TransportDescription* transport,
2230 std::vector<JsepIceCandidate*>* candidates,
2231 webrtc::SdpParseError* error) {
2232 C* media_desc = new C();
2233 switch (media_type) {
2234 case cricket::MEDIA_TYPE_AUDIO:
2235 *content_name = cricket::CN_AUDIO;
2236 break;
2237 case cricket::MEDIA_TYPE_VIDEO:
2238 *content_name = cricket::CN_VIDEO;
2239 break;
2240 case cricket::MEDIA_TYPE_DATA:
2241 *content_name = cricket::CN_DATA;
2242 break;
2243 default:
2244 ASSERT(false);
2245 break;
2246 }
deadbeef67cf2c12016-04-13 10:07:16 -07002247 if (!ParseContent(message, media_type, mline_index, protocol, payload_types,
deadbeef25ed4352016-12-12 18:37:36 -08002248 pos, content_name, bundle_only, media_desc, transport,
2249 candidates, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002250 delete media_desc;
2251 return NULL;
2252 }
2253 // Sort the codecs according to the m-line fmt list.
deadbeef67cf2c12016-04-13 10:07:16 -07002254 std::unordered_map<int, int> payload_type_preferences;
2255 // "size + 1" so that the lowest preference payload type has a preference of
2256 // 1, which is greater than the default (0) for payload types not in the fmt
2257 // list.
2258 int preference = static_cast<int>(payload_types.size() + 1);
2259 for (int pt : payload_types) {
2260 payload_type_preferences[pt] = preference--;
2261 }
2262 std::vector<typename C::CodecType> codecs = media_desc->codecs();
2263 std::sort(codecs.begin(), codecs.end(), [&payload_type_preferences](
2264 const typename C::CodecType& a,
2265 const typename C::CodecType& b) {
2266 return payload_type_preferences[a.id] > payload_type_preferences[b.id];
2267 });
2268 media_desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002269 return media_desc;
2270}
2271
2272bool ParseMediaDescription(const std::string& message,
2273 const TransportDescription& session_td,
2274 const RtpHeaderExtensions& session_extmaps,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002275 size_t* pos,
2276 cricket::SessionDescription* desc,
2277 std::vector<JsepIceCandidate*>* candidates,
2278 SdpParseError* error) {
2279 ASSERT(desc != NULL);
2280 std::string line;
2281 int mline_index = -1;
2282
2283 // Zero or more media descriptions
2284 // RFC 4566
2285 // m=<media> <port> <proto> <fmt>
2286 while (GetLineWithType(message, pos, &line, kLineTypeMedia)) {
2287 ++mline_index;
2288
2289 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002290 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002291 kSdpDelimiterSpace, &fields);
2292 const size_t expected_min_fields = 4;
2293 if (fields.size() < expected_min_fields) {
2294 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2295 }
deadbeef25ed4352016-12-12 18:37:36 -08002296 bool port_rejected = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002297 // RFC 3264
2298 // To reject an offered stream, the port number in the corresponding stream
2299 // in the answer MUST be set to zero.
2300 if (fields[1] == kMediaPortRejected) {
deadbeef25ed4352016-12-12 18:37:36 -08002301 port_rejected = true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002302 }
2303
2304 std::string protocol = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002305
2306 // <fmt>
deadbeef67cf2c12016-04-13 10:07:16 -07002307 std::vector<int> payload_types;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002308 if (IsRtp(protocol)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002309 for (size_t j = 3 ; j < fields.size(); ++j) {
2310 // TODO(wu): Remove when below bug is fixed.
2311 // https://bugzilla.mozilla.org/show_bug.cgi?id=996329
pbosbb36fdf2015-07-09 07:48:14 -07002312 if (fields[j].empty() && j == fields.size() - 1) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002313 continue;
2314 }
wu@webrtc.org36eda7c2014-04-15 20:37:30 +00002315
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002316 int pl = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00002317 if (!GetPayloadTypeFromString(line, fields[j], &pl, error)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002318 return false;
2319 }
deadbeef67cf2c12016-04-13 10:07:16 -07002320 payload_types.push_back(pl);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002321 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002322 }
2323
2324 // Make a temporary TransportDescription based on |session_td|.
2325 // Some of this gets overwritten by ParseContent.
deadbeef46eed762016-01-28 13:24:37 -08002326 TransportDescription transport(
2327 session_td.transport_options, session_td.ice_ufrag, session_td.ice_pwd,
2328 session_td.ice_mode, session_td.connection_role,
2329 session_td.identity_fingerprint.get());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002330
kwibergd1fe2812016-04-27 06:47:29 -07002331 std::unique_ptr<MediaContentDescription> content;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002332 std::string content_name;
deadbeef25ed4352016-12-12 18:37:36 -08002333 bool bundle_only = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002334 if (HasAttribute(line, kMediaTypeVideo)) {
2335 content.reset(ParseContentDescription<VideoContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002336 message, cricket::MEDIA_TYPE_VIDEO, mline_index, protocol,
deadbeef25ed4352016-12-12 18:37:36 -08002337 payload_types, pos, &content_name, &bundle_only, &transport,
2338 candidates, error));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002339 } else if (HasAttribute(line, kMediaTypeAudio)) {
2340 content.reset(ParseContentDescription<AudioContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002341 message, cricket::MEDIA_TYPE_AUDIO, mline_index, protocol,
deadbeef25ed4352016-12-12 18:37:36 -08002342 payload_types, pos, &content_name, &bundle_only, &transport,
2343 candidates, error));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002344 } else if (HasAttribute(line, kMediaTypeData)) {
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002345 DataContentDescription* data_desc =
wu@webrtc.org78187522013-10-07 23:32:02 +00002346 ParseContentDescription<DataContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002347 message, cricket::MEDIA_TYPE_DATA, mline_index, protocol,
deadbeef25ed4352016-12-12 18:37:36 -08002348 payload_types, pos, &content_name, &bundle_only, &transport,
2349 candidates, error);
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002350 content.reset(data_desc);
wu@webrtc.org78187522013-10-07 23:32:02 +00002351
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002352 int p;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002353 if (data_desc && IsDtlsSctp(protocol) && rtc::FromString(fields[3], &p)) {
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002354 if (!AddSctpDataCodec(data_desc, p))
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002355 return false;
wu@webrtc.org78187522013-10-07 23:32:02 +00002356 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002357 } else {
2358 LOG(LS_WARNING) << "Unsupported media type: " << line;
2359 continue;
2360 }
2361 if (!content.get()) {
2362 // ParseContentDescription returns NULL if failed.
2363 return false;
2364 }
2365
deadbeef25ed4352016-12-12 18:37:36 -08002366 bool content_rejected = false;
2367 if (bundle_only) {
2368 // A port of 0 is not interpreted as a rejected m= section when it's
2369 // used along with a=bundle-only.
2370 if (!port_rejected) {
2371 return ParseFailed(
2372 "a=bundle-only",
2373 "a=bundle-only MUST only be used in combination with a 0 port.",
2374 error);
2375 }
2376 } else {
2377 // If not using bundle-only, interpret port 0 in the normal way; the m=
2378 // section is being rejected.
2379 content_rejected = port_rejected;
2380 }
2381
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002382 if (IsRtp(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002383 // Set the extmap.
2384 if (!session_extmaps.empty() &&
2385 !content->rtp_header_extensions().empty()) {
2386 return ParseFailed("",
2387 "The a=extmap MUST be either all session level or "
2388 "all media level.",
2389 error);
2390 }
2391 for (size_t i = 0; i < session_extmaps.size(); ++i) {
2392 content->AddRtpHeaderExtension(session_extmaps[i]);
2393 }
2394 }
2395 content->set_protocol(protocol);
2396 desc->AddContent(content_name,
deadbeef25ed4352016-12-12 18:37:36 -08002397 IsDtlsSctp(protocol) ? cricket::NS_JINGLE_DRAFT_SCTP
2398 : cricket::NS_JINGLE_RTP,
2399 content_rejected, bundle_only, content.release());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002400 // Create TransportInfo with the media level "ice-pwd" and "ice-ufrag".
2401 TransportInfo transport_info(content_name, transport);
2402
2403 if (!desc->AddTransportInfo(transport_info)) {
2404 std::ostringstream description;
2405 description << "Failed to AddTransportInfo with content name: "
2406 << content_name;
2407 return ParseFailed("", description.str(), error);
2408 }
2409 }
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00002410
2411 size_t end_of_message = message.size();
2412 if (mline_index == -1 && *pos != end_of_message) {
2413 ParseFailed(message, *pos, "Expects m line.", error);
2414 return false;
2415 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002416 return true;
2417}
2418
2419bool VerifyCodec(const cricket::Codec& codec) {
2420 // Codec has not been populated correctly unless the name has been set. This
2421 // can happen if an SDP has an fmtp or rtcp-fb with a payload type but doesn't
2422 // have a corresponding "rtpmap" line.
htab39db842016-12-08 01:50:48 -08002423 return !codec.name.empty();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002424}
2425
2426bool VerifyAudioCodecs(const AudioContentDescription* audio_desc) {
2427 const std::vector<cricket::AudioCodec>& codecs = audio_desc->codecs();
2428 for (std::vector<cricket::AudioCodec>::const_iterator iter = codecs.begin();
2429 iter != codecs.end(); ++iter) {
2430 if (!VerifyCodec(*iter)) {
2431 return false;
2432 }
2433 }
2434 return true;
2435}
2436
2437bool VerifyVideoCodecs(const VideoContentDescription* video_desc) {
2438 const std::vector<cricket::VideoCodec>& codecs = video_desc->codecs();
2439 for (std::vector<cricket::VideoCodec>::const_iterator iter = codecs.begin();
2440 iter != codecs.end(); ++iter) {
2441 if (!VerifyCodec(*iter)) {
2442 return false;
2443 }
2444 }
2445 return true;
2446}
2447
2448void AddParameters(const cricket::CodecParameterMap& parameters,
2449 cricket::Codec* codec) {
2450 for (cricket::CodecParameterMap::const_iterator iter =
2451 parameters.begin(); iter != parameters.end(); ++iter) {
2452 codec->SetParam(iter->first, iter->second);
2453 }
2454}
2455
2456void AddFeedbackParameter(const cricket::FeedbackParam& feedback_param,
2457 cricket::Codec* codec) {
2458 codec->AddFeedbackParam(feedback_param);
2459}
2460
2461void AddFeedbackParameters(const cricket::FeedbackParams& feedback_params,
2462 cricket::Codec* codec) {
2463 for (std::vector<cricket::FeedbackParam>::const_iterator iter =
2464 feedback_params.params().begin();
2465 iter != feedback_params.params().end(); ++iter) {
2466 codec->AddFeedbackParam(*iter);
2467 }
2468}
2469
2470// Gets the current codec setting associated with |payload_type|. If there
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002471// is no Codec associated with that payload type it returns an empty codec
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002472// with that payload type.
2473template <class T>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002474T GetCodecWithPayloadType(const std::vector<T>& codecs, int payload_type) {
magjedb05fa242016-11-11 04:00:16 -08002475 const T* codec = FindCodecById(codecs, payload_type);
2476 if (codec)
2477 return *codec;
2478 // Return empty codec with |payload_type|.
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002479 T ret_val;
magjedb05fa242016-11-11 04:00:16 -08002480 ret_val.id = payload_type;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002481 return ret_val;
2482}
2483
2484// Updates or creates a new codec entry in the audio description.
2485template <class T, class U>
2486void AddOrReplaceCodec(MediaContentDescription* content_desc, const U& codec) {
2487 T* desc = static_cast<T*>(content_desc);
2488 std::vector<U> codecs = desc->codecs();
2489 bool found = false;
2490
2491 typename std::vector<U>::iterator iter;
2492 for (iter = codecs.begin(); iter != codecs.end(); ++iter) {
2493 if (iter->id == codec.id) {
2494 *iter = codec;
2495 found = true;
2496 break;
2497 }
2498 }
2499 if (!found) {
2500 desc->AddCodec(codec);
2501 return;
2502 }
2503 desc->set_codecs(codecs);
2504}
2505
2506// Adds or updates existing codec corresponding to |payload_type| according
2507// to |parameters|.
2508template <class T, class U>
2509void UpdateCodec(MediaContentDescription* content_desc, int payload_type,
2510 const cricket::CodecParameterMap& parameters) {
2511 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002512 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2513 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002514 AddParameters(parameters, &new_codec);
2515 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2516}
2517
2518// Adds or updates existing codec corresponding to |payload_type| according
2519// to |feedback_param|.
2520template <class T, class U>
2521void UpdateCodec(MediaContentDescription* content_desc, int payload_type,
2522 const cricket::FeedbackParam& feedback_param) {
2523 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002524 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2525 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002526 AddFeedbackParameter(feedback_param, &new_codec);
2527 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2528}
2529
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002530template <class T>
2531bool PopWildcardCodec(std::vector<T>* codecs, T* wildcard_codec) {
2532 for (auto iter = codecs->begin(); iter != codecs->end(); ++iter) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002533 if (iter->id == kWildcardPayloadType) {
2534 *wildcard_codec = *iter;
2535 codecs->erase(iter);
2536 return true;
2537 }
2538 }
2539 return false;
2540}
2541
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002542template<class T>
2543void UpdateFromWildcardCodecs(cricket::MediaContentDescriptionImpl<T>* desc) {
2544 auto codecs = desc->codecs();
2545 T wildcard_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002546 if (!PopWildcardCodec(&codecs, &wildcard_codec)) {
2547 return;
2548 }
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002549 for (auto& codec : codecs) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002550 AddFeedbackParameters(wildcard_codec.feedback_params, &codec);
2551 }
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002552 desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002553}
2554
2555void AddAudioAttribute(const std::string& name, const std::string& value,
2556 AudioContentDescription* audio_desc) {
2557 if (value.empty()) {
2558 return;
2559 }
2560 std::vector<cricket::AudioCodec> codecs = audio_desc->codecs();
2561 for (std::vector<cricket::AudioCodec>::iterator iter = codecs.begin();
2562 iter != codecs.end(); ++iter) {
2563 iter->params[name] = value;
2564 }
2565 audio_desc->set_codecs(codecs);
2566}
2567
2568bool ParseContent(const std::string& message,
2569 const MediaType media_type,
2570 int mline_index,
2571 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -07002572 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002573 size_t* pos,
2574 std::string* content_name,
deadbeef25ed4352016-12-12 18:37:36 -08002575 bool* bundle_only,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002576 MediaContentDescription* media_desc,
2577 TransportDescription* transport,
2578 std::vector<JsepIceCandidate*>* candidates,
2579 SdpParseError* error) {
2580 ASSERT(media_desc != NULL);
2581 ASSERT(content_name != NULL);
2582 ASSERT(transport != NULL);
2583
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +00002584 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
2585 MaybeCreateStaticPayloadAudioCodecs(
deadbeef67cf2c12016-04-13 10:07:16 -07002586 payload_types, static_cast<AudioContentDescription*>(media_desc));
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +00002587 }
2588
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002589 // The media level "ice-ufrag" and "ice-pwd".
2590 // The candidates before update the media level "ice-pwd" and "ice-ufrag".
2591 Candidates candidates_orig;
2592 std::string line;
2593 std::string mline_id;
2594 // Tracks created out of the ssrc attributes.
2595 StreamParamsVec tracks;
2596 SsrcInfoVec ssrc_infos;
2597 SsrcGroupVec ssrc_groups;
2598 std::string maxptime_as_string;
2599 std::string ptime_as_string;
deadbeef9d3584c2016-02-16 17:54:10 -08002600 std::string stream_id;
2601 std::string track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002602
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002603 // Loop until the next m line
2604 while (!IsLineType(message, kLineTypeMedia, *pos)) {
2605 if (!GetLine(message, pos, &line)) {
2606 if (*pos >= message.size()) {
2607 break; // Done parsing
2608 } else {
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +00002609 return ParseFailed(message, *pos, "Invalid SDP line.", error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002610 }
2611 }
2612
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002613 // RFC 4566
2614 // b=* (zero or more bandwidth information lines)
2615 if (IsLineType(line, kLineTypeSessionBandwidth)) {
2616 std::string bandwidth;
2617 if (HasAttribute(line, kApplicationSpecificMaximum)) {
2618 if (!GetValue(line, kApplicationSpecificMaximum, &bandwidth, error)) {
2619 return false;
2620 } else {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002621 int b = 0;
2622 if (!GetValueFromString(line, bandwidth, &b, error)) {
2623 return false;
2624 }
Peter Thatcherc0c3a862015-06-24 15:31:25 -07002625 // We should never use more than the default bandwidth for RTP-based
2626 // data channels. Don't allow SDP to set the bandwidth, because
2627 // that would give JS the opportunity to "break the Internet".
2628 // See: https://code.google.com/p/chromium/issues/detail?id=280726
2629 if (media_type == cricket::MEDIA_TYPE_DATA && IsRtp(protocol) &&
2630 b > cricket::kDataMaxBandwidth / 1000) {
2631 std::ostringstream description;
2632 description << "RTP-based data channels may not send more than "
2633 << cricket::kDataMaxBandwidth / 1000 << "kbps.";
2634 return ParseFailed(line, description.str(), error);
2635 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002636 media_desc->set_bandwidth(b * 1000);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002637 }
2638 }
2639 continue;
2640 }
2641
2642 if (!IsLineType(line, kLineTypeAttributes)) {
2643 // TODO: Handle other lines if needed.
2644 LOG(LS_INFO) << "Ignored line: " << line;
2645 continue;
2646 }
2647
2648 // Handle attributes common to SCTP and RTP.
2649 if (HasAttribute(line, kAttributeMid)) {
2650 // RFC 3388
2651 // mid-attribute = "a=mid:" identification-tag
2652 // identification-tag = token
2653 // Use the mid identification-tag as the content name.
2654 if (!GetValue(line, kAttributeMid, &mline_id, error)) {
2655 return false;
2656 }
2657 *content_name = mline_id;
deadbeef25ed4352016-12-12 18:37:36 -08002658 } else if (HasAttribute(line, kAttributeBundleOnly)) {
2659 *bundle_only = true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002660 } else if (HasAttribute(line, kAttributeCandidate)) {
2661 Candidate candidate;
2662 if (!ParseCandidate(line, &candidate, error, false)) {
2663 return false;
2664 }
2665 candidates_orig.push_back(candidate);
2666 } else if (HasAttribute(line, kAttributeIceUfrag)) {
2667 if (!GetValue(line, kAttributeIceUfrag, &transport->ice_ufrag, error)) {
2668 return false;
2669 }
2670 } else if (HasAttribute(line, kAttributeIcePwd)) {
2671 if (!GetValue(line, kAttributeIcePwd, &transport->ice_pwd, error)) {
2672 return false;
2673 }
2674 } else if (HasAttribute(line, kAttributeIceOption)) {
2675 if (!ParseIceOptions(line, &transport->transport_options, error)) {
2676 return false;
2677 }
2678 } else if (HasAttribute(line, kAttributeFmtp)) {
2679 if (!ParseFmtpAttributes(line, media_type, media_desc, error)) {
2680 return false;
2681 }
2682 } else if (HasAttribute(line, kAttributeFingerprint)) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002683 rtc::SSLFingerprint* fingerprint = NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002684
2685 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
2686 return false;
2687 }
2688 transport->identity_fingerprint.reset(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002689 } else if (HasAttribute(line, kAttributeSetup)) {
2690 if (!ParseDtlsSetup(line, &(transport->connection_role), error)) {
2691 return false;
2692 }
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002693 } else if (IsDtlsSctp(protocol) && HasAttribute(line, kAttributeSctpPort)) {
deadbeef7e146cb2016-09-28 10:04:34 -07002694 if (media_type != cricket::MEDIA_TYPE_DATA) {
2695 return ParseFailed(
2696 line, "sctp-port attribute found in non-data media description.",
2697 error);
2698 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002699 int sctp_port;
2700 if (!ParseSctpPort(line, &sctp_port, error)) {
2701 return false;
2702 }
2703 if (!AddSctpDataCodec(static_cast<DataContentDescription*>(media_desc),
2704 sctp_port)) {
2705 return false;
2706 }
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002707 } else if (IsRtp(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002708 //
2709 // RTP specific attrubtes
2710 //
2711 if (HasAttribute(line, kAttributeRtcpMux)) {
2712 media_desc->set_rtcp_mux(true);
deadbeef13871492015-12-09 12:37:51 -08002713 } else if (HasAttribute(line, kAttributeRtcpReducedSize)) {
2714 media_desc->set_rtcp_reduced_size(true);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002715 } else if (HasAttribute(line, kAttributeSsrcGroup)) {
2716 if (!ParseSsrcGroupAttribute(line, &ssrc_groups, error)) {
2717 return false;
2718 }
2719 } else if (HasAttribute(line, kAttributeSsrc)) {
2720 if (!ParseSsrcAttribute(line, &ssrc_infos, error)) {
2721 return false;
2722 }
2723 } else if (HasAttribute(line, kAttributeCrypto)) {
2724 if (!ParseCryptoAttribute(line, media_desc, error)) {
2725 return false;
2726 }
2727 } else if (HasAttribute(line, kAttributeRtpmap)) {
deadbeef67cf2c12016-04-13 10:07:16 -07002728 if (!ParseRtpmapAttribute(line, media_type, payload_types, media_desc,
2729 error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002730 return false;
2731 }
2732 } else if (HasAttribute(line, kCodecParamMaxPTime)) {
2733 if (!GetValue(line, kCodecParamMaxPTime, &maxptime_as_string, error)) {
2734 return false;
2735 }
2736 } else if (HasAttribute(line, kAttributeRtcpFb)) {
2737 if (!ParseRtcpFbAttribute(line, media_type, media_desc, error)) {
2738 return false;
2739 }
2740 } else if (HasAttribute(line, kCodecParamPTime)) {
2741 if (!GetValue(line, kCodecParamPTime, &ptime_as_string, error)) {
2742 return false;
2743 }
2744 } else if (HasAttribute(line, kAttributeSendOnly)) {
2745 media_desc->set_direction(cricket::MD_SENDONLY);
2746 } else if (HasAttribute(line, kAttributeRecvOnly)) {
2747 media_desc->set_direction(cricket::MD_RECVONLY);
2748 } else if (HasAttribute(line, kAttributeInactive)) {
2749 media_desc->set_direction(cricket::MD_INACTIVE);
2750 } else if (HasAttribute(line, kAttributeSendRecv)) {
2751 media_desc->set_direction(cricket::MD_SENDRECV);
2752 } else if (HasAttribute(line, kAttributeExtmap)) {
isheriff6f8d6862016-05-26 11:24:55 -07002753 RtpExtension extmap;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002754 if (!ParseExtmap(line, &extmap, error)) {
2755 return false;
2756 }
2757 media_desc->AddRtpHeaderExtension(extmap);
2758 } else if (HasAttribute(line, kAttributeXGoogleFlag)) {
2759 // Experimental attribute. Conference mode activates more aggressive
2760 // AEC and NS settings.
2761 // TODO: expose API to set these directly.
2762 std::string flag_value;
2763 if (!GetValue(line, kAttributeXGoogleFlag, &flag_value, error)) {
2764 return false;
2765 }
2766 if (flag_value.compare(kValueConference) == 0)
2767 media_desc->set_conference_mode(true);
deadbeef9d3584c2016-02-16 17:54:10 -08002768 } else if (HasAttribute(line, kAttributeMsid)) {
2769 if (!ParseMsidAttribute(line, &stream_id, &track_id, error)) {
2770 return false;
2771 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002772 }
2773 } else {
2774 // Only parse lines that we are interested of.
2775 LOG(LS_INFO) << "Ignored line: " << line;
2776 continue;
2777 }
2778 }
2779
2780 // Create tracks from the |ssrc_infos|.
Taylor Brandstetter5de6b752016-03-09 17:02:30 -08002781 // If the stream_id/track_id for all SSRCS are identical, one StreamParams
2782 // will be created in CreateTracksFromSsrcInfos, containing all the SSRCs from
2783 // the m= section.
2784 CreateTracksFromSsrcInfos(ssrc_infos, stream_id, track_id, &tracks);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002785
2786 // Add the ssrc group to the track.
2787 for (SsrcGroupVec::iterator ssrc_group = ssrc_groups.begin();
2788 ssrc_group != ssrc_groups.end(); ++ssrc_group) {
2789 if (ssrc_group->ssrcs.empty()) {
2790 continue;
2791 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002792 uint32_t ssrc = ssrc_group->ssrcs.front();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002793 for (StreamParamsVec::iterator track = tracks.begin();
2794 track != tracks.end(); ++track) {
2795 if (track->has_ssrc(ssrc)) {
2796 track->ssrc_groups.push_back(*ssrc_group);
2797 }
2798 }
2799 }
2800
2801 // Add the new tracks to the |media_desc|.
deadbeef9d3584c2016-02-16 17:54:10 -08002802 for (StreamParams& track : tracks) {
2803 media_desc->AddStream(track);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002804 }
2805
2806 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
2807 AudioContentDescription* audio_desc =
2808 static_cast<AudioContentDescription*>(media_desc);
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002809 UpdateFromWildcardCodecs(audio_desc);
2810
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002811 // Verify audio codec ensures that no audio codec has been populated with
2812 // only fmtp.
2813 if (!VerifyAudioCodecs(audio_desc)) {
2814 return ParseFailed("Failed to parse audio codecs correctly.", error);
2815 }
2816 AddAudioAttribute(kCodecParamMaxPTime, maxptime_as_string, audio_desc);
2817 AddAudioAttribute(kCodecParamPTime, ptime_as_string, audio_desc);
2818 }
2819
2820 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002821 VideoContentDescription* video_desc =
2822 static_cast<VideoContentDescription*>(media_desc);
2823 UpdateFromWildcardCodecs(video_desc);
2824 // Verify video codec ensures that no video codec has been populated with
2825 // only rtcp-fb.
2826 if (!VerifyVideoCodecs(video_desc)) {
2827 return ParseFailed("Failed to parse video codecs correctly.", error);
2828 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002829 }
2830
2831 // RFC 5245
2832 // Update the candidates with the media level "ice-pwd" and "ice-ufrag".
2833 for (Candidates::iterator it = candidates_orig.begin();
2834 it != candidates_orig.end(); ++it) {
honghaiza54a0802015-12-16 18:37:23 -08002835 ASSERT((*it).username().empty() ||
2836 (*it).username() == transport->ice_ufrag);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002837 (*it).set_username(transport->ice_ufrag);
2838 ASSERT((*it).password().empty());
2839 (*it).set_password(transport->ice_pwd);
2840 candidates->push_back(
2841 new JsepIceCandidate(mline_id, mline_index, *it));
2842 }
2843 return true;
2844}
2845
2846bool ParseSsrcAttribute(const std::string& line, SsrcInfoVec* ssrc_infos,
2847 SdpParseError* error) {
2848 ASSERT(ssrc_infos != NULL);
2849 // RFC 5576
2850 // a=ssrc:<ssrc-id> <attribute>
2851 // a=ssrc:<ssrc-id> <attribute>:<value>
2852 std::string field1, field2;
Donald Curtis144d0182015-05-15 13:14:24 -07002853 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
2854 &field1, &field2)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002855 const size_t expected_fields = 2;
2856 return ParseFailedExpectFieldNum(line, expected_fields, error);
2857 }
2858
2859 // ssrc:<ssrc-id>
2860 std::string ssrc_id_s;
2861 if (!GetValue(field1, kAttributeSsrc, &ssrc_id_s, error)) {
2862 return false;
2863 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002864 uint32_t ssrc_id = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002865 if (!GetValueFromString(line, ssrc_id_s, &ssrc_id, error)) {
2866 return false;
2867 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002868
2869 std::string attribute;
2870 std::string value;
Donald Curtis144d0182015-05-15 13:14:24 -07002871 if (!rtc::tokenize_first(field2, kSdpDelimiterColon, &attribute, &value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002872 std::ostringstream description;
2873 description << "Failed to get the ssrc attribute value from " << field2
2874 << ". Expected format <attribute>:<value>.";
2875 return ParseFailed(line, description.str(), error);
2876 }
2877
2878 // Check if there's already an item for this |ssrc_id|. Create a new one if
2879 // there isn't.
2880 SsrcInfoVec::iterator ssrc_info = ssrc_infos->begin();
2881 for (; ssrc_info != ssrc_infos->end(); ++ssrc_info) {
2882 if (ssrc_info->ssrc_id == ssrc_id) {
2883 break;
2884 }
2885 }
2886 if (ssrc_info == ssrc_infos->end()) {
2887 SsrcInfo info;
2888 info.ssrc_id = ssrc_id;
2889 ssrc_infos->push_back(info);
2890 ssrc_info = ssrc_infos->end() - 1;
2891 }
2892
2893 // Store the info to the |ssrc_info|.
2894 if (attribute == kSsrcAttributeCname) {
2895 // RFC 5576
2896 // cname:<value>
2897 ssrc_info->cname = value;
2898 } else if (attribute == kSsrcAttributeMsid) {
2899 // draft-alvestrand-mmusic-msid-00
2900 // "msid:" identifier [ " " appdata ]
2901 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002902 rtc::split(value, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002903 if (fields.size() < 1 || fields.size() > 2) {
2904 return ParseFailed(line,
2905 "Expected format \"msid:<identifier>[ <appdata>]\".",
2906 error);
2907 }
deadbeef9d3584c2016-02-16 17:54:10 -08002908 ssrc_info->stream_id = fields[0];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002909 if (fields.size() == 2) {
deadbeef9d3584c2016-02-16 17:54:10 -08002910 ssrc_info->track_id = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002911 }
2912 } else if (attribute == kSsrcAttributeMslabel) {
2913 // draft-alvestrand-rtcweb-mid-01
2914 // mslabel:<value>
2915 ssrc_info->mslabel = value;
2916 } else if (attribute == kSSrcAttributeLabel) {
2917 // The label isn't defined.
2918 // label:<value>
2919 ssrc_info->label = value;
2920 }
2921 return true;
2922}
2923
2924bool ParseSsrcGroupAttribute(const std::string& line,
2925 SsrcGroupVec* ssrc_groups,
2926 SdpParseError* error) {
2927 ASSERT(ssrc_groups != NULL);
2928 // RFC 5576
2929 // a=ssrc-group:<semantics> <ssrc-id> ...
2930 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002931 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002932 kSdpDelimiterSpace, &fields);
2933 const size_t expected_min_fields = 2;
2934 if (fields.size() < expected_min_fields) {
2935 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2936 }
2937 std::string semantics;
2938 if (!GetValue(fields[0], kAttributeSsrcGroup, &semantics, error)) {
2939 return false;
2940 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002941 std::vector<uint32_t> ssrcs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002942 for (size_t i = 1; i < fields.size(); ++i) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02002943 uint32_t ssrc = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002944 if (!GetValueFromString(line, fields[i], &ssrc, error)) {
2945 return false;
2946 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002947 ssrcs.push_back(ssrc);
2948 }
2949 ssrc_groups->push_back(SsrcGroup(semantics, ssrcs));
2950 return true;
2951}
2952
2953bool ParseCryptoAttribute(const std::string& line,
2954 MediaContentDescription* media_desc,
2955 SdpParseError* error) {
2956 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002957 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002958 kSdpDelimiterSpace, &fields);
2959 // RFC 4568
2960 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
2961 const size_t expected_min_fields = 3;
2962 if (fields.size() < expected_min_fields) {
2963 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2964 }
2965 std::string tag_value;
2966 if (!GetValue(fields[0], kAttributeCrypto, &tag_value, error)) {
2967 return false;
2968 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002969 int tag = 0;
2970 if (!GetValueFromString(line, tag_value, &tag, error)) {
2971 return false;
2972 }
jbauch083b73f2015-07-16 02:46:32 -07002973 const std::string& crypto_suite = fields[1];
2974 const std::string& key_params = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002975 std::string session_params;
2976 if (fields.size() > 3) {
2977 session_params = fields[3];
2978 }
2979 media_desc->AddCrypto(CryptoParams(tag, crypto_suite, key_params,
2980 session_params));
2981 return true;
2982}
2983
2984// Updates or creates a new codec entry in the audio description with according
deadbeef67cf2c12016-04-13 10:07:16 -07002985// to |name|, |clockrate|, |bitrate|, and |channels|.
2986void UpdateCodec(int payload_type,
2987 const std::string& name,
2988 int clockrate,
2989 int bitrate,
2990 size_t channels,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002991 AudioContentDescription* audio_desc) {
2992 // Codec may already be populated with (only) optional parameters
2993 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002994 cricket::AudioCodec codec =
2995 GetCodecWithPayloadType(audio_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002996 codec.name = name;
2997 codec.clockrate = clockrate;
2998 codec.bitrate = bitrate;
2999 codec.channels = channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003000 AddOrReplaceCodec<AudioContentDescription, cricket::AudioCodec>(audio_desc,
3001 codec);
3002}
3003
3004// Updates or creates a new codec entry in the video description according to
deadbeef67cf2c12016-04-13 10:07:16 -07003005// |name|, |width|, |height|, and |framerate|.
3006void UpdateCodec(int payload_type,
3007 const std::string& name,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003008 VideoContentDescription* video_desc) {
3009 // Codec may already be populated with (only) optional parameters
3010 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00003011 cricket::VideoCodec codec =
3012 GetCodecWithPayloadType(video_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003013 codec.name = name;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003014 AddOrReplaceCodec<VideoContentDescription, cricket::VideoCodec>(video_desc,
3015 codec);
3016}
3017
3018bool ParseRtpmapAttribute(const std::string& line,
3019 const MediaType media_type,
deadbeef67cf2c12016-04-13 10:07:16 -07003020 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003021 MediaContentDescription* media_desc,
3022 SdpParseError* error) {
3023 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003024 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003025 kSdpDelimiterSpace, &fields);
3026 // RFC 4566
3027 // a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encodingparameters>]
3028 const size_t expected_min_fields = 2;
3029 if (fields.size() < expected_min_fields) {
3030 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
3031 }
3032 std::string payload_type_value;
3033 if (!GetValue(fields[0], kAttributeRtpmap, &payload_type_value, error)) {
3034 return false;
3035 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003036 int payload_type = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00003037 if (!GetPayloadTypeFromString(line, payload_type_value, &payload_type,
3038 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003039 return false;
3040 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003041
deadbeef67cf2c12016-04-13 10:07:16 -07003042 if (std::find(payload_types.begin(), payload_types.end(), payload_type) ==
3043 payload_types.end()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003044 LOG(LS_WARNING) << "Ignore rtpmap line that did not appear in the "
3045 << "<fmt> of the m-line: " << line;
3046 return true;
3047 }
jbauch083b73f2015-07-16 02:46:32 -07003048 const std::string& encoder = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003049 std::vector<std::string> codec_params;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003050 rtc::split(encoder, '/', &codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003051 // <encoding name>/<clock rate>[/<encodingparameters>]
3052 // 2 mandatory fields
3053 if (codec_params.size() < 2 || codec_params.size() > 3) {
3054 return ParseFailed(line,
3055 "Expected format \"<encoding name>/<clock rate>"
3056 "[/<encodingparameters>]\".",
3057 error);
3058 }
jbauch083b73f2015-07-16 02:46:32 -07003059 const std::string& encoding_name = codec_params[0];
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003060 int clock_rate = 0;
3061 if (!GetValueFromString(line, codec_params[1], &clock_rate, error)) {
3062 return false;
3063 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003064 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3065 VideoContentDescription* video_desc =
3066 static_cast<VideoContentDescription*>(media_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003067 UpdateCodec(payload_type, encoding_name,
deadbeef67cf2c12016-04-13 10:07:16 -07003068 video_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003069 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3070 // RFC 4566
3071 // For audio streams, <encoding parameters> indicates the number
3072 // of audio channels. This parameter is OPTIONAL and may be
3073 // omitted if the number of channels is one, provided that no
3074 // additional parameters are needed.
Peter Kasting69558702016-01-12 16:26:35 -08003075 size_t channels = 1;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003076 if (codec_params.size() == 3) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003077 if (!GetValueFromString(line, codec_params[2], &channels, error)) {
3078 return false;
3079 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003080 }
3081 int bitrate = 0;
3082 // The default behavior for ISAC (bitrate == 0) in webrtcvoiceengine.cc
3083 // (specifically FindWebRtcCodec) is bandwidth-adaptive variable bitrate.
3084 // The bandwidth adaptation doesn't always work well, so this code
3085 // sets a fixed target bitrate instead.
3086 if (_stricmp(encoding_name.c_str(), kIsacCodecName) == 0) {
3087 if (clock_rate <= 16000) {
3088 bitrate = kIsacWbDefaultRate;
3089 } else {
3090 bitrate = kIsacSwbDefaultRate;
3091 }
3092 }
3093 AudioContentDescription* audio_desc =
3094 static_cast<AudioContentDescription*>(media_desc);
3095 UpdateCodec(payload_type, encoding_name, clock_rate, bitrate, channels,
deadbeef67cf2c12016-04-13 10:07:16 -07003096 audio_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003097 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
3098 DataContentDescription* data_desc =
3099 static_cast<DataContentDescription*>(media_desc);
deadbeef67cf2c12016-04-13 10:07:16 -07003100 data_desc->AddCodec(cricket::DataCodec(payload_type, encoding_name));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003101 }
3102 return true;
3103}
3104
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003105bool ParseFmtpParam(const std::string& line, std::string* parameter,
3106 std::string* value, SdpParseError* error) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003107 if (!rtc::tokenize_first(line, kSdpDelimiterEqual, parameter, value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003108 ParseFailed(line, "Unable to parse fmtp parameter. \'=\' missing.", error);
3109 return false;
3110 }
3111 // a=fmtp:<payload_type> <param1>=<value1>; <param2>=<value2>; ...
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003112 return true;
3113}
3114
3115bool ParseFmtpAttributes(const std::string& line, const MediaType media_type,
3116 MediaContentDescription* media_desc,
3117 SdpParseError* error) {
3118 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3119 media_type != cricket::MEDIA_TYPE_VIDEO) {
3120 return true;
3121 }
Donald Curtis0e07f922015-05-15 09:21:23 -07003122
3123 std::string line_payload;
3124 std::string line_params;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003125
3126 // RFC 5576
3127 // a=fmtp:<format> <format specific parameters>
3128 // At least two fields, whereas the second one is any of the optional
3129 // parameters.
Donald Curtis144d0182015-05-15 13:14:24 -07003130 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
3131 &line_payload, &line_params)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003132 ParseFailedExpectMinFieldNum(line, 2, error);
3133 return false;
3134 }
3135
Donald Curtis0e07f922015-05-15 09:21:23 -07003136 // Parse out the payload information.
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003137 std::string payload_type_str;
Donald Curtis0e07f922015-05-15 09:21:23 -07003138 if (!GetValue(line_payload, kAttributeFmtp, &payload_type_str, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003139 return false;
3140 }
3141
Donald Curtis0e07f922015-05-15 09:21:23 -07003142 int payload_type = 0;
Donald Curtis144d0182015-05-15 13:14:24 -07003143 if (!GetPayloadTypeFromString(line_payload, payload_type_str, &payload_type,
3144 error)) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003145 return false;
3146 }
3147
3148 // Parse out format specific parameters.
3149 std::vector<std::string> fields;
3150 rtc::split(line_params, kSdpDelimiterSemicolon, &fields);
3151
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003152 cricket::CodecParameterMap codec_params;
Donald Curtis144d0182015-05-15 13:14:24 -07003153 for (auto& iter : fields) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003154 if (iter.find(kSdpDelimiterEqual) == std::string::npos) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003155 // Only fmtps with equals are currently supported. Other fmtp types
3156 // should be ignored. Unknown fmtps do not constitute an error.
3157 continue;
3158 }
Donald Curtis0e07f922015-05-15 09:21:23 -07003159
3160 std::string name;
3161 std::string value;
3162 if (!ParseFmtpParam(rtc::string_trim(iter), &name, &value, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003163 return false;
3164 }
3165 codec_params[name] = value;
3166 }
3167
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003168 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3169 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003170 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003171 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3172 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003173 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003174 }
3175 return true;
3176}
3177
3178bool ParseRtcpFbAttribute(const std::string& line, const MediaType media_type,
3179 MediaContentDescription* media_desc,
3180 SdpParseError* error) {
3181 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3182 media_type != cricket::MEDIA_TYPE_VIDEO) {
3183 return true;
3184 }
3185 std::vector<std::string> rtcp_fb_fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003186 rtc::split(line.c_str(), kSdpDelimiterSpace, &rtcp_fb_fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003187 if (rtcp_fb_fields.size() < 2) {
3188 return ParseFailedGetValue(line, kAttributeRtcpFb, error);
3189 }
3190 std::string payload_type_string;
3191 if (!GetValue(rtcp_fb_fields[0], kAttributeRtcpFb, &payload_type_string,
3192 error)) {
3193 return false;
3194 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003195 int payload_type = kWildcardPayloadType;
3196 if (payload_type_string != "*") {
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00003197 if (!GetPayloadTypeFromString(line, payload_type_string, &payload_type,
3198 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003199 return false;
3200 }
3201 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003202 std::string id = rtcp_fb_fields[1];
3203 std::string param = "";
3204 for (std::vector<std::string>::iterator iter = rtcp_fb_fields.begin() + 2;
3205 iter != rtcp_fb_fields.end(); ++iter) {
3206 param.append(*iter);
3207 }
3208 const cricket::FeedbackParam feedback_param(id, param);
3209
3210 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003211 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
3212 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003213 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003214 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
3215 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003216 }
3217 return true;
3218}
3219
3220} // namespace webrtc