blob: 28c8be07e21352c0bef5a1e7e0bac09cc76f4b80 [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";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000111static const char kAttributeRtcpMux[] = "rtcp-mux";
deadbeef13871492015-12-09 12:37:51 -0800112static const char kAttributeRtcpReducedSize[] = "rtcp-rsize";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000113static const char kAttributeSsrc[] = "ssrc";
114static const char kSsrcAttributeCname[] = "cname";
115static const char kAttributeExtmap[] = "extmap";
116// draft-alvestrand-mmusic-msid-01
117// a=msid-semantic: WMS
118static const char kAttributeMsidSemantics[] = "msid-semantic";
119static const char kMediaStreamSemantic[] = "WMS";
120static const char kSsrcAttributeMsid[] = "msid";
121static const char kDefaultMsid[] = "default";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000122static const char kSsrcAttributeMslabel[] = "mslabel";
123static const char kSSrcAttributeLabel[] = "label";
124static const char kAttributeSsrcGroup[] = "ssrc-group";
125static const char kAttributeCrypto[] = "crypto";
126static const char kAttributeCandidate[] = "candidate";
127static const char kAttributeCandidateTyp[] = "typ";
128static const char kAttributeCandidateRaddr[] = "raddr";
129static const char kAttributeCandidateRport[] = "rport";
honghaiza54a0802015-12-16 18:37:23 -0800130static const char kAttributeCandidateUfrag[] = "ufrag";
131static const char kAttributeCandidatePwd[] = "pwd";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000132static const char kAttributeCandidateGeneration[] = "generation";
honghaiza0c44ea2016-03-23 16:07:48 -0700133static const char kAttributeCandidateNetworkId[] = "network-id";
honghaize1a0c942016-02-16 14:54:56 -0800134static const char kAttributeCandidateNetworkCost[] = "network-cost";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000135static const char kAttributeFingerprint[] = "fingerprint";
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000136static const char kAttributeSetup[] = "setup";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000137static const char kAttributeFmtp[] = "fmtp";
138static const char kAttributeRtpmap[] = "rtpmap";
wu@webrtc.org78187522013-10-07 23:32:02 +0000139static const char kAttributeSctpmap[] = "sctpmap";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000140static const char kAttributeRtcp[] = "rtcp";
141static const char kAttributeIceUfrag[] = "ice-ufrag";
142static const char kAttributeIcePwd[] = "ice-pwd";
143static const char kAttributeIceLite[] = "ice-lite";
144static const char kAttributeIceOption[] = "ice-options";
145static const char kAttributeSendOnly[] = "sendonly";
146static const char kAttributeRecvOnly[] = "recvonly";
147static const char kAttributeRtcpFb[] = "rtcp-fb";
148static const char kAttributeSendRecv[] = "sendrecv";
149static const char kAttributeInactive[] = "inactive";
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +0000150// draft-ietf-mmusic-sctp-sdp-07
151// a=sctp-port
152static const char kAttributeSctpPort[] = "sctp-port";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000153
154// Experimental flags
155static const char kAttributeXGoogleFlag[] = "x-google-flag";
156static const char kValueConference[] = "conference";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000157
158// Candidate
159static const char kCandidateHost[] = "host";
160static const char kCandidateSrflx[] = "srflx";
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700161static const char kCandidatePrflx[] = "prflx";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000162static const char kCandidateRelay[] = "relay";
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +0000163static const char kTcpCandidateType[] = "tcptype";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000164
165static const char kSdpDelimiterEqual = '=';
166static const char kSdpDelimiterSpace = ' ';
167static const char kSdpDelimiterColon = ':';
168static const char kSdpDelimiterSemicolon = ';';
169static const char kSdpDelimiterSlash = '/';
170static const char kNewLine = '\n';
171static const char kReturn = '\r';
172static const char kLineBreak[] = "\r\n";
173
174// TODO: Generate the Session and Time description
175// instead of hardcoding.
176static const char kSessionVersion[] = "v=0";
177// RFC 4566
178static const char kSessionOriginUsername[] = "-";
179static const char kSessionOriginSessionId[] = "0";
180static const char kSessionOriginSessionVersion[] = "0";
181static const char kSessionOriginNettype[] = "IN";
182static const char kSessionOriginAddrtype[] = "IP4";
183static const char kSessionOriginAddress[] = "127.0.0.1";
184static const char kSessionName[] = "s=-";
185static const char kTimeDescription[] = "t=0 0";
186static const char kAttrGroup[] = "a=group:BUNDLE";
187static const char kConnectionNettype[] = "IN";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000188static const char kConnectionIpv4Addrtype[] = "IP4";
189static const char kConnectionIpv6Addrtype[] = "IP6";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000190static const char kMediaTypeVideo[] = "video";
191static const char kMediaTypeAudio[] = "audio";
192static const char kMediaTypeData[] = "application";
193static const char kMediaPortRejected[] = "0";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000194// draft-ietf-mmusic-trickle-ice-01
195// When no candidates have been gathered, set the connection
196// address to IP6 ::.
perkj@webrtc.orgd105cc82014-11-07 11:22:06 +0000197// TODO(perkj): FF can not parse IP6 ::. See http://crbug/430333
198// Use IPV4 per default.
199static const char kDummyAddress[] = "0.0.0.0";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000200static const char kDummyPort[] = "9";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000201// RFC 3556
202static const char kApplicationSpecificMaximum[] = "AS";
203
204static const int kDefaultVideoClockrate = 90000;
205
206// ISAC special-case.
207static const char kIsacCodecName[] = "ISAC"; // From webrtcvoiceengine.cc
208static const int kIsacWbDefaultRate = 32000; // From acm_common_defs.h
209static const int kIsacSwbDefaultRate = 56000; // From acm_common_defs.h
210
wu@webrtc.org78187522013-10-07 23:32:02 +0000211static const char kDefaultSctpmapProtocol[] = "webrtc-datachannel";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000212
pkasting@chromium.orgd3245462015-02-23 21:28:22 +0000213// RTP payload type is in the 0-127 range. Use -1 to indicate "all" payload
214// types.
215const int kWildcardPayloadType = -1;
216
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000217struct SsrcInfo {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200218 uint32_t ssrc_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000219 std::string cname;
deadbeef9d3584c2016-02-16 17:54:10 -0800220 std::string stream_id;
221 std::string track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000222
223 // For backward compatibility.
224 // TODO(ronghuawu): Remove below 2 fields once all the clients support msid.
225 std::string label;
226 std::string mslabel;
227};
228typedef std::vector<SsrcInfo> SsrcInfoVec;
229typedef std::vector<SsrcGroup> SsrcGroupVec;
230
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000231template <class T>
232static void AddFmtpLine(const T& codec, std::string* message);
233static void BuildMediaDescription(const ContentInfo* content_info,
234 const TransportInfo* transport_info,
235 const MediaType media_type,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000236 const std::vector<Candidate>& candidates,
deadbeef9d3584c2016-02-16 17:54:10 -0800237 bool unified_plan_sdp,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000238 std::string* message);
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000239static void BuildSctpContentAttributes(std::string* message, int sctp_port);
deadbeef9d3584c2016-02-16 17:54:10 -0800240static void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
241 const MediaType media_type,
242 bool unified_plan_sdp,
243 std::string* message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000244static void BuildRtpMap(const MediaContentDescription* media_desc,
245 const MediaType media_type,
246 std::string* message);
247static void BuildCandidate(const std::vector<Candidate>& candidates,
honghaiza54a0802015-12-16 18:37:23 -0800248 bool include_ufrag,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000249 std::string* message);
250static void BuildIceOptions(const std::vector<std::string>& transport_options,
251 std::string* message);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +0000252static bool IsRtp(const std::string& protocol);
253static bool IsDtlsSctp(const std::string& protocol);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000254static bool ParseSessionDescription(const std::string& message, size_t* pos,
255 std::string* session_id,
256 std::string* session_version,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000257 TransportDescription* session_td,
258 RtpHeaderExtensions* session_extmaps,
259 cricket::SessionDescription* desc,
260 SdpParseError* error);
261static bool ParseGroupAttribute(const std::string& line,
262 cricket::SessionDescription* desc,
263 SdpParseError* error);
264static bool ParseMediaDescription(
265 const std::string& message,
266 const TransportDescription& session_td,
267 const RtpHeaderExtensions& session_extmaps,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000268 size_t* pos, cricket::SessionDescription* desc,
269 std::vector<JsepIceCandidate*>* candidates,
270 SdpParseError* error);
271static bool ParseContent(const std::string& message,
272 const MediaType media_type,
273 int mline_index,
274 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -0700275 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000276 size_t* pos,
277 std::string* content_name,
278 MediaContentDescription* media_desc,
279 TransportDescription* transport,
280 std::vector<JsepIceCandidate*>* candidates,
281 SdpParseError* error);
282static bool ParseSsrcAttribute(const std::string& line,
283 SsrcInfoVec* ssrc_infos,
284 SdpParseError* error);
285static bool ParseSsrcGroupAttribute(const std::string& line,
286 SsrcGroupVec* ssrc_groups,
287 SdpParseError* error);
288static bool ParseCryptoAttribute(const std::string& line,
289 MediaContentDescription* media_desc,
290 SdpParseError* error);
291static bool ParseRtpmapAttribute(const std::string& line,
292 const MediaType media_type,
deadbeef67cf2c12016-04-13 10:07:16 -0700293 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000294 MediaContentDescription* media_desc,
295 SdpParseError* error);
296static bool ParseFmtpAttributes(const std::string& line,
297 const MediaType media_type,
298 MediaContentDescription* media_desc,
299 SdpParseError* error);
300static bool ParseFmtpParam(const std::string& line, std::string* parameter,
301 std::string* value, SdpParseError* error);
302static bool ParseCandidate(const std::string& message, Candidate* candidate,
303 SdpParseError* error, bool is_raw);
304static bool ParseRtcpFbAttribute(const std::string& line,
305 const MediaType media_type,
306 MediaContentDescription* media_desc,
307 SdpParseError* error);
308static bool ParseIceOptions(const std::string& line,
309 std::vector<std::string>* transport_options,
310 SdpParseError* error);
311static bool ParseExtmap(const std::string& line,
isheriff6f8d6862016-05-26 11:24:55 -0700312 RtpExtension* extmap,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000313 SdpParseError* error);
314static bool ParseFingerprintAttribute(const std::string& line,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000315 rtc::SSLFingerprint** fingerprint,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000316 SdpParseError* error);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000317static bool ParseDtlsSetup(const std::string& line,
318 cricket::ConnectionRole* role,
319 SdpParseError* error);
deadbeef9d3584c2016-02-16 17:54:10 -0800320static bool ParseMsidAttribute(const std::string& line,
321 std::string* stream_id,
322 std::string* track_id,
323 SdpParseError* error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000324
325// Helper functions
326
327// Below ParseFailed*** functions output the line that caused the parsing
328// failure and the detailed reason (|description|) of the failure to |error|.
329// The functions always return false so that they can be used directly in the
330// following way when error happens:
331// "return ParseFailed***(...);"
332
333// The line starting at |line_start| of |message| is the failing line.
334// The reason for the failure should be provided in the |description|.
335// An example of a description could be "unknown character".
336static bool ParseFailed(const std::string& message,
337 size_t line_start,
338 const std::string& description,
339 SdpParseError* error) {
340 // Get the first line of |message| from |line_start|.
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000341 std::string first_line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000342 size_t line_end = message.find(kNewLine, line_start);
343 if (line_end != std::string::npos) {
344 if (line_end > 0 && (message.at(line_end - 1) == kReturn)) {
345 --line_end;
346 }
347 first_line = message.substr(line_start, (line_end - line_start));
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000348 } else {
349 first_line = message.substr(line_start);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000350 }
351
352 if (error) {
353 error->line = first_line;
354 error->description = description;
355 }
356 LOG(LS_ERROR) << "Failed to parse: \"" << first_line
357 << "\". Reason: " << description;
358 return false;
359}
360
361// |line| is the failing line. The reason for the failure should be
362// provided in the |description|.
363static bool ParseFailed(const std::string& line,
364 const std::string& description,
365 SdpParseError* error) {
366 return ParseFailed(line, 0, description, error);
367}
368
369// Parses failure where the failing SDP line isn't know or there are multiple
370// failing lines.
371static bool ParseFailed(const std::string& description,
372 SdpParseError* error) {
373 return ParseFailed("", description, error);
374}
375
376// |line| is the failing line. The failure is due to the fact that |line|
377// doesn't have |expected_fields| fields.
378static bool ParseFailedExpectFieldNum(const std::string& line,
379 int expected_fields,
380 SdpParseError* error) {
381 std::ostringstream description;
382 description << "Expects " << expected_fields << " fields.";
383 return ParseFailed(line, description.str(), error);
384}
385
386// |line| is the failing line. The failure is due to the fact that |line| has
387// less than |expected_min_fields| fields.
388static bool ParseFailedExpectMinFieldNum(const std::string& line,
389 int expected_min_fields,
390 SdpParseError* error) {
391 std::ostringstream description;
392 description << "Expects at least " << expected_min_fields << " fields.";
393 return ParseFailed(line, description.str(), error);
394}
395
396// |line| is the failing line. The failure is due to the fact that it failed to
397// get the value of |attribute|.
398static bool ParseFailedGetValue(const std::string& line,
399 const std::string& attribute,
400 SdpParseError* error) {
401 std::ostringstream description;
402 description << "Failed to get the value of attribute: " << attribute;
403 return ParseFailed(line, description.str(), error);
404}
405
406// The line starting at |line_start| of |message| is the failing line. The
407// failure is due to the line type (e.g. the "m" part of the "m-line")
408// not matching what is expected. The expected line type should be
409// provided as |line_type|.
410static bool ParseFailedExpectLine(const std::string& message,
411 size_t line_start,
412 const char line_type,
413 const std::string& line_value,
414 SdpParseError* error) {
415 std::ostringstream description;
416 description << "Expect line: " << line_type << "=" << line_value;
417 return ParseFailed(message, line_start, description.str(), error);
418}
419
420static bool AddLine(const std::string& line, std::string* message) {
421 if (!message)
422 return false;
423
424 message->append(line);
425 message->append(kLineBreak);
426 return true;
427}
428
429static bool GetLine(const std::string& message,
430 size_t* pos,
431 std::string* line) {
432 size_t line_begin = *pos;
433 size_t line_end = message.find(kNewLine, line_begin);
434 if (line_end == std::string::npos) {
435 return false;
436 }
437 // Update the new start position
438 *pos = line_end + 1;
439 if (line_end > 0 && (message.at(line_end - 1) == kReturn)) {
440 --line_end;
441 }
442 *line = message.substr(line_begin, (line_end - line_begin));
443 const char* cline = line->c_str();
444 // RFC 4566
445 // An SDP session description consists of a number of lines of text of
446 // the form:
447 // <type>=<value>
448 // where <type> MUST be exactly one case-significant character and
449 // <value> is structured text whose format depends on <type>.
450 // Whitespace MUST NOT be used on either side of the "=" sign.
decurtis@webrtc.org8af11042015-01-07 19:15:51 +0000451 if (line->length() < 3 ||
452 !islower(cline[0]) ||
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000453 cline[1] != kSdpDelimiterEqual ||
454 cline[2] == kSdpDelimiterSpace) {
455 *pos = line_begin;
456 return false;
457 }
458 return true;
459}
460
461// Init |os| to "|type|=|value|".
462static void InitLine(const char type,
463 const std::string& value,
464 std::ostringstream* os) {
465 os->str("");
466 *os << type << kSdpDelimiterEqual << value;
467}
468
469// Init |os| to "a=|attribute|".
470static void InitAttrLine(const std::string& attribute, std::ostringstream* os) {
471 InitLine(kLineTypeAttributes, attribute, os);
472}
473
474// Writes a SDP attribute line based on |attribute| and |value| to |message|.
475static void AddAttributeLine(const std::string& attribute, int value,
476 std::string* message) {
477 std::ostringstream os;
478 InitAttrLine(attribute, &os);
479 os << kSdpDelimiterColon << value;
480 AddLine(os.str(), message);
481}
482
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000483static bool IsLineType(const std::string& message,
484 const char type,
485 size_t line_start) {
486 if (message.size() < line_start + kLinePrefixLength) {
487 return false;
488 }
489 const char* cmessage = message.c_str();
490 return (cmessage[line_start] == type &&
491 cmessage[line_start + 1] == kSdpDelimiterEqual);
492}
493
494static bool IsLineType(const std::string& line,
495 const char type) {
496 return IsLineType(line, type, 0);
497}
498
499static bool GetLineWithType(const std::string& message, size_t* pos,
500 std::string* line, const char type) {
501 if (!IsLineType(message, type, *pos)) {
502 return false;
503 }
504
505 if (!GetLine(message, pos, line))
506 return false;
507
508 return true;
509}
510
511static bool HasAttribute(const std::string& line,
512 const std::string& attribute) {
513 return (line.compare(kLinePrefixLength, attribute.size(), attribute) == 0);
514}
515
Peter Boström0c4e06b2015-10-07 12:23:21 +0200516static bool AddSsrcLine(uint32_t ssrc_id,
517 const std::string& attribute,
518 const std::string& value,
519 std::string* message) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000520 // RFC 5576
521 // a=ssrc:<ssrc-id> <attribute>:<value>
522 std::ostringstream os;
523 InitAttrLine(kAttributeSsrc, &os);
524 os << kSdpDelimiterColon << ssrc_id << kSdpDelimiterSpace
525 << attribute << kSdpDelimiterColon << value;
526 return AddLine(os.str(), message);
527}
528
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000529// Get value only from <attribute>:<value>.
530static bool GetValue(const std::string& message, const std::string& attribute,
531 std::string* value, SdpParseError* error) {
532 std::string leftpart;
Donald Curtis0e07f922015-05-15 09:21:23 -0700533 if (!rtc::tokenize_first(message, kSdpDelimiterColon, &leftpart, value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000534 return ParseFailedGetValue(message, attribute, error);
535 }
536 // The left part should end with the expected attribute.
537 if (leftpart.length() < attribute.length() ||
538 leftpart.compare(leftpart.length() - attribute.length(),
539 attribute.length(), attribute) != 0) {
540 return ParseFailedGetValue(message, attribute, error);
541 }
542 return true;
543}
544
545static bool CaseInsensitiveFind(std::string str1, std::string str2) {
546 std::transform(str1.begin(), str1.end(), str1.begin(),
547 ::tolower);
548 std::transform(str2.begin(), str2.end(), str2.begin(),
549 ::tolower);
550 return str1.find(str2) != std::string::npos;
551}
552
wu@webrtc.org5e760e72014-04-02 23:19:09 +0000553template <class T>
554static bool GetValueFromString(const std::string& line,
555 const std::string& s,
556 T* t,
557 SdpParseError* error) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000558 if (!rtc::FromString(s, t)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +0000559 std::ostringstream description;
560 description << "Invalid value: " << s << ".";
561 return ParseFailed(line, description.str(), error);
562 }
563 return true;
564}
565
pkasting@chromium.orge9facf82015-02-17 20:36:28 +0000566static bool GetPayloadTypeFromString(const std::string& line,
567 const std::string& s,
568 int* payload_type,
569 SdpParseError* error) {
570 return GetValueFromString(line, s, payload_type, error) &&
571 cricket::IsValidRtpPayloadType(*payload_type);
572}
573
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800574// |msid_stream_id| and |msid_track_id| represent the stream/track ID from the
575// "a=msid" attribute, if it exists. They are empty if the attribute does not
576// exist.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000577void CreateTracksFromSsrcInfos(const SsrcInfoVec& ssrc_infos,
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800578 const std::string& msid_stream_id,
579 const std::string& msid_track_id,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000580 StreamParamsVec* tracks) {
581 ASSERT(tracks != NULL);
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800582 ASSERT(msid_stream_id.empty() == msid_track_id.empty());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000583 for (SsrcInfoVec::const_iterator ssrc_info = ssrc_infos.begin();
584 ssrc_info != ssrc_infos.end(); ++ssrc_info) {
585 if (ssrc_info->cname.empty()) {
586 continue;
587 }
588
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800589 std::string stream_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000590 std::string track_id;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800591 if (ssrc_info->stream_id.empty() && !ssrc_info->mslabel.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000592 // If there's no msid and there's mslabel, we consider this is a sdp from
593 // a older version of client that doesn't support msid.
594 // In that case, we use the mslabel and label to construct the track.
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800595 stream_id = ssrc_info->mslabel;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000596 track_id = ssrc_info->label;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800597 } else if (ssrc_info->stream_id.empty() && !msid_stream_id.empty()) {
598 // If there's no msid in the SSRC attributes, but there's a global one
599 // (from a=msid), use that. This is the case with unified plan SDP.
600 stream_id = msid_stream_id;
601 track_id = msid_track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000602 } else {
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800603 stream_id = ssrc_info->stream_id;
deadbeef9d3584c2016-02-16 17:54:10 -0800604 track_id = ssrc_info->track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000605 }
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800606 // If a stream/track ID wasn't populated from the SSRC attributes OR the
607 // msid attribute, use default/random values.
608 if (stream_id.empty()) {
609 stream_id = kDefaultMsid;
610 }
611 if (track_id.empty()) {
612 // TODO(ronghuawu): What should we do if the track id doesn't appear?
613 // Create random string (which will be used as track label later)?
614 track_id = rtc::CreateRandomString(8);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000615 }
616
617 StreamParamsVec::iterator track = tracks->begin();
618 for (; track != tracks->end(); ++track) {
619 if (track->id == track_id) {
620 break;
621 }
622 }
623 if (track == tracks->end()) {
624 // If we don't find an existing track, create a new one.
625 tracks->push_back(StreamParams());
626 track = tracks->end() - 1;
627 }
628 track->add_ssrc(ssrc_info->ssrc_id);
629 track->cname = ssrc_info->cname;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800630 track->sync_label = stream_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000631 track->id = track_id;
632 }
633}
634
635void GetMediaStreamLabels(const ContentInfo* content,
636 std::set<std::string>* labels) {
637 const MediaContentDescription* media_desc =
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000638 static_cast<const MediaContentDescription*>(
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000639 content->description);
640 const cricket::StreamParamsVec& streams = media_desc->streams();
641 for (cricket::StreamParamsVec::const_iterator it = streams.begin();
642 it != streams.end(); ++it) {
643 labels->insert(it->sync_label);
644 }
645}
646
647// RFC 5245
648// It is RECOMMENDED that default candidates be chosen based on the
649// likelihood of those candidates to work with the peer that is being
650// contacted. It is RECOMMENDED that relayed > reflexive > host.
651static const int kPreferenceUnknown = 0;
652static const int kPreferenceHost = 1;
653static const int kPreferenceReflexive = 2;
654static const int kPreferenceRelayed = 3;
655
656static int GetCandidatePreferenceFromType(const std::string& type) {
657 int preference = kPreferenceUnknown;
658 if (type == cricket::LOCAL_PORT_TYPE) {
659 preference = kPreferenceHost;
660 } else if (type == cricket::STUN_PORT_TYPE) {
661 preference = kPreferenceReflexive;
662 } else if (type == cricket::RELAY_PORT_TYPE) {
663 preference = kPreferenceRelayed;
664 } else {
665 ASSERT(false);
666 }
667 return preference;
668}
669
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000670// Get ip and port of the default destination from the |candidates| with the
671// given value of |component_id|. The default candidate should be the one most
672// likely to work, typically IPv4 relay.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000673// RFC 5245
674// The value of |component_id| currently supported are 1 (RTP) and 2 (RTCP).
675// TODO: Decide the default destination in webrtcsession and
676// pass it down via SessionDescription.
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000677static void GetDefaultDestination(
678 const std::vector<Candidate>& candidates,
679 int component_id, std::string* port,
680 std::string* ip, std::string* addr_type) {
perkj@webrtc.orgd105cc82014-11-07 11:22:06 +0000681 *addr_type = kConnectionIpv4Addrtype;
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000682 *port = kDummyPort;
683 *ip = kDummyAddress;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000684 int current_preference = kPreferenceUnknown;
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000685 int current_family = AF_UNSPEC;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000686 for (std::vector<Candidate>::const_iterator it = candidates.begin();
687 it != candidates.end(); ++it) {
688 if (it->component() != component_id) {
689 continue;
690 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000691 // Default destination should be UDP only.
692 if (it->protocol() != cricket::UDP_PROTOCOL_NAME) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000693 continue;
694 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000695 const int preference = GetCandidatePreferenceFromType(it->type());
696 const int family = it->address().ipaddr().family();
697 // See if this candidate is more preferable then the current one if it's the
698 // same family. Or if the current family is IPv4 already so we could safely
699 // ignore all IPv6 ones. WebRTC bug 4269.
700 // http://code.google.com/p/webrtc/issues/detail?id=4269
701 if ((preference <= current_preference && current_family == family) ||
702 (current_family == AF_INET && family == AF_INET6)) {
703 continue;
704 }
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000705 if (family == AF_INET) {
706 addr_type->assign(kConnectionIpv4Addrtype);
707 } else if (family == AF_INET6) {
708 addr_type->assign(kConnectionIpv6Addrtype);
709 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000710 current_preference = preference;
711 current_family = family;
712 *port = it->address().PortAsString();
713 *ip = it->address().ipaddr().ToString();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000714 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000715}
716
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000717// Update |mline|'s default destination and append a c line after it.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000718static void UpdateMediaDefaultDestination(
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000719 const std::vector<Candidate>& candidates,
jbauch083b73f2015-07-16 02:46:32 -0700720 const std::string& mline,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000721 std::string* message) {
722 std::string new_lines;
723 AddLine(mline, &new_lines);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000724 // RFC 4566
725 // m=<media> <port> <proto> <fmt> ...
726 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000727 rtc::split(mline, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000728 if (fields.size() < 3) {
729 return;
730 }
731
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000732 std::ostringstream os;
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000733 std::string rtp_port, rtp_ip, addr_type;
734 GetDefaultDestination(candidates, ICE_CANDIDATE_COMPONENT_RTP,
735 &rtp_port, &rtp_ip, &addr_type);
736 // Found default RTP candidate.
737 // RFC 5245
738 // The default candidates are added to the SDP as the default
739 // destination for media. For streams based on RTP, this is done by
740 // placing the IP address and port of the RTP candidate into the c and m
741 // lines, respectively.
742 // Update the port in the m line.
743 // If this is a m-line with port equal to 0, we don't change it.
744 if (fields[1] != kMediaPortRejected) {
745 new_lines.replace(fields[0].size() + 1,
746 fields[1].size(),
747 rtp_port);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000748 }
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000749 // Add the c line.
750 // RFC 4566
751 // c=<nettype> <addrtype> <connection-address>
752 InitLine(kLineTypeConnection, kConnectionNettype, &os);
753 os << " " << addr_type << " " << rtp_ip;
754 AddLine(os.str(), &new_lines);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000755 message->append(new_lines);
756}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000757
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000758// Gets "a=rtcp" line if found default RTCP candidate from |candidates|.
759static std::string GetRtcpLine(const std::vector<Candidate>& candidates) {
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000760 std::string rtcp_line, rtcp_port, rtcp_ip, addr_type;
761 GetDefaultDestination(candidates, ICE_CANDIDATE_COMPONENT_RTCP,
762 &rtcp_port, &rtcp_ip, &addr_type);
763 // Found default RTCP candidate.
764 // RFC 5245
765 // If the agent is utilizing RTCP, it MUST encode the RTCP candidate
766 // using the a=rtcp attribute as defined in RFC 3605.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000767
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000768 // RFC 3605
769 // rtcp-attribute = "a=rtcp:" port [nettype space addrtype space
770 // connection-address] CRLF
771 std::ostringstream os;
772 InitAttrLine(kAttributeRtcp, &os);
773 os << kSdpDelimiterColon
774 << rtcp_port << " "
775 << kConnectionNettype << " "
776 << addr_type << " "
777 << rtcp_ip;
778 rtcp_line = os.str();
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000779 return rtcp_line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000780}
781
782// Get candidates according to the mline index from SessionDescriptionInterface.
783static void GetCandidatesByMindex(const SessionDescriptionInterface& desci,
784 int mline_index,
785 std::vector<Candidate>* candidates) {
786 if (!candidates) {
787 return;
788 }
789 const IceCandidateCollection* cc = desci.candidates(mline_index);
790 for (size_t i = 0; i < cc->count(); ++i) {
791 const IceCandidateInterface* candidate = cc->at(i);
792 candidates->push_back(candidate->candidate());
793 }
794}
795
deadbeef9d3584c2016-02-16 17:54:10 -0800796std::string SdpSerialize(const JsepSessionDescription& jdesc,
797 bool unified_plan_sdp) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000798 const cricket::SessionDescription* desc = jdesc.description();
799 if (!desc) {
800 return "";
801 }
802
803 std::string message;
804
805 // Session Description.
806 AddLine(kSessionVersion, &message);
807 // Session Origin
808 // RFC 4566
809 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
810 // <unicast-address>
811 std::ostringstream os;
812 InitLine(kLineTypeOrigin, kSessionOriginUsername, &os);
jbauch083b73f2015-07-16 02:46:32 -0700813 const std::string& session_id = jdesc.session_id().empty() ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000814 kSessionOriginSessionId : jdesc.session_id();
jbauch083b73f2015-07-16 02:46:32 -0700815 const std::string& session_version = jdesc.session_version().empty() ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000816 kSessionOriginSessionVersion : jdesc.session_version();
817 os << " " << session_id << " " << session_version << " "
818 << kSessionOriginNettype << " " << kSessionOriginAddrtype << " "
819 << kSessionOriginAddress;
820 AddLine(os.str(), &message);
821 AddLine(kSessionName, &message);
822
823 // Time Description.
824 AddLine(kTimeDescription, &message);
825
826 // Group
827 if (desc->HasGroup(cricket::GROUP_TYPE_BUNDLE)) {
828 std::string group_line = kAttrGroup;
829 const cricket::ContentGroup* group =
830 desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
831 ASSERT(group != NULL);
832 const cricket::ContentNames& content_names = group->content_names();
833 for (cricket::ContentNames::const_iterator it = content_names.begin();
834 it != content_names.end(); ++it) {
835 group_line.append(" ");
836 group_line.append(*it);
837 }
838 AddLine(group_line, &message);
839 }
840
841 // MediaStream semantics
842 InitAttrLine(kAttributeMsidSemantics, &os);
843 os << kSdpDelimiterColon << " " << kMediaStreamSemantic;
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000844
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000845 std::set<std::string> media_stream_labels;
846 const ContentInfo* audio_content = GetFirstAudioContent(desc);
847 if (audio_content)
848 GetMediaStreamLabels(audio_content, &media_stream_labels);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000849
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000850 const ContentInfo* video_content = GetFirstVideoContent(desc);
851 if (video_content)
852 GetMediaStreamLabels(video_content, &media_stream_labels);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000853
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000854 for (std::set<std::string>::const_iterator it =
855 media_stream_labels.begin(); it != media_stream_labels.end(); ++it) {
856 os << " " << *it;
857 }
858 AddLine(os.str(), &message);
859
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000860 // Preserve the order of the media contents.
861 int mline_index = -1;
862 for (cricket::ContentInfos::const_iterator it = desc->contents().begin();
863 it != desc->contents().end(); ++it) {
864 const MediaContentDescription* mdesc =
865 static_cast<const MediaContentDescription*>(it->description);
866 std::vector<Candidate> candidates;
867 GetCandidatesByMindex(jdesc, ++mline_index, &candidates);
deadbeef9d3584c2016-02-16 17:54:10 -0800868 BuildMediaDescription(&*it, desc->GetTransportInfoByName(it->name),
869 mdesc->type(), candidates, unified_plan_sdp,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000870 &message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000871 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000872 return message;
873}
874
875// Serializes the passed in IceCandidateInterface to a SDP string.
876// candidate - The candidate to be serialized.
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700877std::string SdpSerializeCandidate(const IceCandidateInterface& candidate) {
878 return SdpSerializeCandidate(candidate.candidate());
879}
880
881// Serializes a cricket Candidate.
882std::string SdpSerializeCandidate(const cricket::Candidate& candidate) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000883 std::string message;
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700884 std::vector<cricket::Candidate> candidates(1, candidate);
honghaiza54a0802015-12-16 18:37:23 -0800885 BuildCandidate(candidates, true, &message);
wu@webrtc.orgec9f5fb2014-06-24 17:05:10 +0000886 // From WebRTC draft section 4.8.1.1 candidate-attribute will be
887 // just candidate:<candidate> not a=candidate:<blah>CRLF
888 ASSERT(message.find("a=") == 0);
889 message.erase(0, 2);
890 ASSERT(message.find(kLineBreak) == message.size() - 2);
891 message.resize(message.size() - 2);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000892 return message;
893}
894
895bool SdpDeserialize(const std::string& message,
896 JsepSessionDescription* jdesc,
897 SdpParseError* error) {
898 std::string session_id;
899 std::string session_version;
Peter Thatcher7cbd1882015-09-17 18:54:52 -0700900 TransportDescription session_td("", "");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000901 RtpHeaderExtensions session_extmaps;
902 cricket::SessionDescription* desc = new cricket::SessionDescription();
903 std::vector<JsepIceCandidate*> candidates;
904 size_t current_pos = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000905
906 // Session Description
907 if (!ParseSessionDescription(message, &current_pos, &session_id,
deadbeefc80741f2015-10-22 13:14:45 -0700908 &session_version, &session_td, &session_extmaps,
909 desc, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000910 delete desc;
911 return false;
912 }
913
914 // Media Description
deadbeefc80741f2015-10-22 13:14:45 -0700915 if (!ParseMediaDescription(message, session_td, session_extmaps, &current_pos,
916 desc, &candidates, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000917 delete desc;
918 for (std::vector<JsepIceCandidate*>::const_iterator
919 it = candidates.begin(); it != candidates.end(); ++it) {
920 delete *it;
921 }
922 return false;
923 }
924
925 jdesc->Initialize(desc, session_id, session_version);
926
927 for (std::vector<JsepIceCandidate*>::const_iterator
928 it = candidates.begin(); it != candidates.end(); ++it) {
929 jdesc->AddCandidate(*it);
930 delete *it;
931 }
932 return true;
933}
934
935bool SdpDeserializeCandidate(const std::string& message,
936 JsepIceCandidate* jcandidate,
937 SdpParseError* error) {
938 ASSERT(jcandidate != NULL);
939 Candidate candidate;
940 if (!ParseCandidate(message, &candidate, error, true)) {
941 return false;
942 }
943 jcandidate->SetCandidate(candidate);
944 return true;
945}
946
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700947bool SdpDeserializeCandidate(const std::string& transport_name,
948 const std::string& message,
949 cricket::Candidate* candidate,
950 SdpParseError* error) {
951 ASSERT(candidate != nullptr);
952 if (!ParseCandidate(message, candidate, error, true)) {
953 return false;
954 }
955 candidate->set_transport_name(transport_name);
956 return true;
957}
958
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000959bool ParseCandidate(const std::string& message, Candidate* candidate,
960 SdpParseError* error, bool is_raw) {
961 ASSERT(candidate != NULL);
962
963 // Get the first line from |message|.
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000964 std::string first_line = message;
965 size_t pos = 0;
966 GetLine(message, &pos, &first_line);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000967
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000968 // Makes sure |message| contains only one line.
969 if (message.size() > first_line.size()) {
970 std::string left, right;
Donald Curtis0e07f922015-05-15 09:21:23 -0700971 if (rtc::tokenize_first(message, kNewLine, &left, &right) &&
972 !right.empty()) {
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000973 return ParseFailed(message, 0, "Expect one line only", error);
974 }
975 }
976
977 // From WebRTC draft section 4.8.1.1 candidate-attribute should be
978 // candidate:<candidate> when trickled, but we still support
979 // a=candidate:<blah>CRLF for backward compatibility and for parsing a line
980 // from the SDP.
981 if (IsLineType(first_line, kLineTypeAttributes)) {
982 first_line = first_line.substr(kLinePrefixLength);
983 }
984
985 std::string attribute_candidate;
986 std::string candidate_value;
987
988 // |first_line| must be in the form of "candidate:<value>".
Donald Curtis144d0182015-05-15 13:14:24 -0700989 if (!rtc::tokenize_first(first_line, kSdpDelimiterColon, &attribute_candidate,
990 &candidate_value) ||
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000991 attribute_candidate != kAttributeCandidate) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000992 if (is_raw) {
993 std::ostringstream description;
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000994 description << "Expect line: " << kAttributeCandidate
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000995 << ":" << "<candidate-str>";
996 return ParseFailed(first_line, 0, description.str(), error);
997 } else {
998 return ParseFailedExpectLine(first_line, 0, kLineTypeAttributes,
999 kAttributeCandidate, error);
1000 }
1001 }
1002
1003 std::vector<std::string> fields;
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001004 rtc::split(candidate_value, kSdpDelimiterSpace, &fields);
1005
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001006 // RFC 5245
1007 // a=candidate:<foundation> <component-id> <transport> <priority>
1008 // <connection-address> <port> typ <candidate-types>
1009 // [raddr <connection-address>] [rport <port>]
1010 // *(SP extension-att-name SP extension-att-value)
1011 const size_t expected_min_fields = 8;
1012 if (fields.size() < expected_min_fields ||
1013 (fields[6] != kAttributeCandidateTyp)) {
1014 return ParseFailedExpectMinFieldNum(first_line, expected_min_fields, error);
1015 }
jbauch083b73f2015-07-16 02:46:32 -07001016 const std::string& foundation = fields[0];
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001017
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001018 int component_id = 0;
1019 if (!GetValueFromString(first_line, fields[1], &component_id, error)) {
1020 return false;
1021 }
jbauch083b73f2015-07-16 02:46:32 -07001022 const std::string& transport = fields[2];
Peter Boström0c4e06b2015-10-07 12:23:21 +02001023 uint32_t priority = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001024 if (!GetValueFromString(first_line, fields[3], &priority, error)) {
1025 return false;
1026 }
jbauch083b73f2015-07-16 02:46:32 -07001027 const std::string& connection_address = fields[4];
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001028 int port = 0;
1029 if (!GetValueFromString(first_line, fields[5], &port, error)) {
1030 return false;
1031 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001032 SocketAddress address(connection_address, port);
1033
1034 cricket::ProtocolType protocol;
1035 if (!StringToProto(transport.c_str(), &protocol)) {
1036 return ParseFailed(first_line, "Unsupported transport type.", error);
1037 }
1038
1039 std::string candidate_type;
jbauch083b73f2015-07-16 02:46:32 -07001040 const std::string& type = fields[7];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001041 if (type == kCandidateHost) {
1042 candidate_type = cricket::LOCAL_PORT_TYPE;
1043 } else if (type == kCandidateSrflx) {
1044 candidate_type = cricket::STUN_PORT_TYPE;
1045 } else if (type == kCandidateRelay) {
1046 candidate_type = cricket::RELAY_PORT_TYPE;
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001047 } else if (type == kCandidatePrflx) {
1048 candidate_type = cricket::PRFLX_PORT_TYPE;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001049 } else {
1050 return ParseFailed(first_line, "Unsupported candidate type.", error);
1051 }
1052
1053 size_t current_position = expected_min_fields;
1054 SocketAddress related_address;
1055 // The 2 optional fields for related address
1056 // [raddr <connection-address>] [rport <port>]
1057 if (fields.size() >= (current_position + 2) &&
1058 fields[current_position] == kAttributeCandidateRaddr) {
1059 related_address.SetIP(fields[++current_position]);
1060 ++current_position;
1061 }
1062 if (fields.size() >= (current_position + 2) &&
1063 fields[current_position] == kAttributeCandidateRport) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001064 int port = 0;
1065 if (!GetValueFromString(
1066 first_line, fields[++current_position], &port, error)) {
1067 return false;
1068 }
1069 related_address.SetPort(port);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001070 ++current_position;
1071 }
1072
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001073 // If this is a TCP candidate, it has additional extension as defined in
1074 // RFC 6544.
1075 std::string tcptype;
1076 if (fields.size() >= (current_position + 2) &&
1077 fields[current_position] == kTcpCandidateType) {
1078 tcptype = fields[++current_position];
1079 ++current_position;
1080
1081 if (tcptype != cricket::TCPTYPE_ACTIVE_STR &&
1082 tcptype != cricket::TCPTYPE_PASSIVE_STR &&
1083 tcptype != cricket::TCPTYPE_SIMOPEN_STR) {
1084 return ParseFailed(first_line, "Invalid TCP candidate type.", error);
1085 }
1086
1087 if (protocol != cricket::PROTO_TCP) {
1088 return ParseFailed(first_line, "Invalid non-TCP candidate", error);
1089 }
1090 }
1091
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001092 // Extension
honghaiza54a0802015-12-16 18:37:23 -08001093 // Though non-standard, we support the ICE ufrag and pwd being signaled on
1094 // the candidate to avoid issues with confusing which generation a candidate
1095 // belongs to when trickling multiple generations at the same time.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001096 std::string username;
1097 std::string password;
Peter Boström0c4e06b2015-10-07 12:23:21 +02001098 uint32_t generation = 0;
honghaiza0c44ea2016-03-23 16:07:48 -07001099 uint16_t network_id = 0;
1100 uint16_t network_cost = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001101 for (size_t i = current_position; i + 1 < fields.size(); ++i) {
1102 // RFC 5245
1103 // *(SP extension-att-name SP extension-att-value)
1104 if (fields[i] == kAttributeCandidateGeneration) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001105 if (!GetValueFromString(first_line, fields[++i], &generation, error)) {
1106 return false;
1107 }
honghaiza54a0802015-12-16 18:37:23 -08001108 } else if (fields[i] == kAttributeCandidateUfrag) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001109 username = fields[++i];
honghaiza54a0802015-12-16 18:37:23 -08001110 } else if (fields[i] == kAttributeCandidatePwd) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001111 password = fields[++i];
honghaiza0c44ea2016-03-23 16:07:48 -07001112 } else if (fields[i] == kAttributeCandidateNetworkId) {
1113 if (!GetValueFromString(first_line, fields[++i], &network_id, error)) {
1114 return false;
1115 }
honghaize1a0c942016-02-16 14:54:56 -08001116 } else if (fields[i] == kAttributeCandidateNetworkCost) {
1117 if (!GetValueFromString(first_line, fields[++i], &network_cost, error)) {
1118 return false;
1119 }
Honghai Zhang351d77b2016-05-20 15:08:29 -07001120 network_cost = std::min(network_cost, rtc::kNetworkCostMax);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001121 } else {
1122 // Skip the unknown extension.
1123 ++i;
1124 }
1125 }
1126
guoweis@webrtc.org61c12472015-01-15 06:53:07 +00001127 *candidate = Candidate(component_id, cricket::ProtoToString(protocol),
guoweis@webrtc.org950c5182014-12-16 23:01:31 +00001128 address, priority, username, password, candidate_type,
honghaiza0c44ea2016-03-23 16:07:48 -07001129 generation, foundation, network_id, network_cost);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001130 candidate->set_related_address(related_address);
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001131 candidate->set_tcptype(tcptype);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001132 return true;
1133}
1134
1135bool ParseIceOptions(const std::string& line,
1136 std::vector<std::string>* transport_options,
1137 SdpParseError* error) {
1138 std::string ice_options;
1139 if (!GetValue(line, kAttributeIceOption, &ice_options, error)) {
1140 return false;
1141 }
1142 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001143 rtc::split(ice_options, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001144 for (size_t i = 0; i < fields.size(); ++i) {
1145 transport_options->push_back(fields[i]);
1146 }
1147 return true;
1148}
1149
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001150bool ParseSctpPort(const std::string& line,
1151 int* sctp_port,
1152 SdpParseError* error) {
1153 // draft-ietf-mmusic-sctp-sdp-07
1154 // a=sctp-port
1155 std::vector<std::string> fields;
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001156 const size_t expected_min_fields = 2;
lally69f57602015-10-08 10:15:04 -07001157 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColon, &fields);
1158 if (fields.size() < expected_min_fields) {
1159 fields.resize(0);
1160 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpace, &fields);
1161 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001162 if (fields.size() < expected_min_fields) {
1163 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1164 }
1165 if (!rtc::FromString(fields[1], sctp_port)) {
lally69f57602015-10-08 10:15:04 -07001166 return ParseFailed(line, "Invalid sctp port value.", error);
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001167 }
1168 return true;
1169}
1170
isheriff6f8d6862016-05-26 11:24:55 -07001171bool ParseExtmap(const std::string& line,
1172 RtpExtension* extmap,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001173 SdpParseError* error) {
1174 // RFC 5285
1175 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1176 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001177 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001178 kSdpDelimiterSpace, &fields);
1179 const size_t expected_min_fields = 2;
1180 if (fields.size() < expected_min_fields) {
1181 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1182 }
1183 std::string uri = fields[1];
1184
1185 std::string value_direction;
1186 if (!GetValue(fields[0], kAttributeExtmap, &value_direction, error)) {
1187 return false;
1188 }
1189 std::vector<std::string> sub_fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001190 rtc::split(value_direction, kSdpDelimiterSlash, &sub_fields);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001191 int value = 0;
1192 if (!GetValueFromString(line, sub_fields[0], &value, error)) {
1193 return false;
1194 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001195
isheriff6f8d6862016-05-26 11:24:55 -07001196 *extmap = RtpExtension(uri, value);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001197 return true;
1198}
1199
1200void BuildMediaDescription(const ContentInfo* content_info,
1201 const TransportInfo* transport_info,
1202 const MediaType media_type,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001203 const std::vector<Candidate>& candidates,
deadbeef9d3584c2016-02-16 17:54:10 -08001204 bool unified_plan_sdp,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001205 std::string* message) {
1206 ASSERT(message != NULL);
1207 if (content_info == NULL || message == NULL) {
1208 return;
1209 }
1210 // TODO: Rethink if we should use sprintfn instead of stringstream.
1211 // According to the style guide, streams should only be used for logging.
1212 // http://google-styleguide.googlecode.com/svn/
1213 // trunk/cppguide.xml?showone=Streams#Streams
1214 std::ostringstream os;
1215 const MediaContentDescription* media_desc =
henrike@webrtc.org28654cb2013-07-22 21:07:49 +00001216 static_cast<const MediaContentDescription*>(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001217 content_info->description);
1218 ASSERT(media_desc != NULL);
1219
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001220 int sctp_port = cricket::kSctpDefaultPort;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001221
1222 // RFC 4566
1223 // m=<media> <port> <proto> <fmt>
1224 // fmt is a list of payload type numbers that MAY be used in the session.
1225 const char* type = NULL;
1226 if (media_type == cricket::MEDIA_TYPE_AUDIO)
1227 type = kMediaTypeAudio;
1228 else if (media_type == cricket::MEDIA_TYPE_VIDEO)
1229 type = kMediaTypeVideo;
1230 else if (media_type == cricket::MEDIA_TYPE_DATA)
1231 type = kMediaTypeData;
1232 else
1233 ASSERT(false);
1234
1235 std::string fmt;
1236 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
1237 const VideoContentDescription* video_desc =
1238 static_cast<const VideoContentDescription*>(media_desc);
1239 for (std::vector<cricket::VideoCodec>::const_iterator it =
1240 video_desc->codecs().begin();
1241 it != video_desc->codecs().end(); ++it) {
1242 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001243 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001244 }
1245 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
1246 const AudioContentDescription* audio_desc =
1247 static_cast<const AudioContentDescription*>(media_desc);
1248 for (std::vector<cricket::AudioCodec>::const_iterator it =
1249 audio_desc->codecs().begin();
1250 it != audio_desc->codecs().end(); ++it) {
1251 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001252 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001253 }
1254 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001255 const DataContentDescription* data_desc =
1256 static_cast<const DataContentDescription*>(media_desc);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001257 if (IsDtlsSctp(media_desc->protocol())) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001258 fmt.append(" ");
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001259
1260 for (std::vector<cricket::DataCodec>::const_iterator it =
1261 data_desc->codecs().begin();
1262 it != data_desc->codecs().end(); ++it) {
solenberg9fa49752016-10-08 13:02:44 -07001263 if (cricket::CodecNamesEq(it->name, cricket::kGoogleSctpDataCodecName)
1264 && it->GetParam(cricket::kCodecParamPort, &sctp_port)) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001265 break;
1266 }
1267 }
1268
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001269 fmt.append(rtc::ToString<int>(sctp_port));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001270 } else {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001271 for (std::vector<cricket::DataCodec>::const_iterator it =
1272 data_desc->codecs().begin();
1273 it != data_desc->codecs().end(); ++it) {
1274 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001275 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001276 }
1277 }
1278 }
1279 // The fmt must never be empty. If no codecs are found, set the fmt attribute
1280 // to 0.
1281 if (fmt.empty()) {
1282 fmt = " 0";
1283 }
1284
1285 // The port number in the m line will be updated later when associate with
1286 // the candidates.
1287 // RFC 3264
1288 // To reject an offered stream, the port number in the corresponding stream in
1289 // the answer MUST be set to zero.
jbauch083b73f2015-07-16 02:46:32 -07001290 const std::string& port = content_info->rejected ?
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +00001291 kMediaPortRejected : kDummyPort;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001292
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001293 rtc::SSLFingerprint* fp = (transport_info) ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001294 transport_info->description.identity_fingerprint.get() : NULL;
1295
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001296 // Add the m and c lines.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001297 InitLine(kLineTypeMedia, type, &os);
1298 os << " " << port << " " << media_desc->protocol() << fmt;
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001299 std::string mline = os.str();
1300 UpdateMediaDefaultDestination(candidates, mline, message);
1301
1302 // RFC 4566
1303 // b=AS:<bandwidth>
Peter Thatcherc0c3a862015-06-24 15:31:25 -07001304 if (media_desc->bandwidth() >= 1000) {
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001305 InitLine(kLineTypeSessionBandwidth, kApplicationSpecificMaximum, &os);
1306 os << kSdpDelimiterColon << (media_desc->bandwidth() / 1000);
1307 AddLine(os.str(), message);
1308 }
1309
1310 // Add the a=rtcp line.
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001311 if (IsRtp(media_desc->protocol())) {
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001312 std::string rtcp_line = GetRtcpLine(candidates);
1313 if (!rtcp_line.empty()) {
1314 AddLine(rtcp_line, message);
1315 }
1316 }
1317
honghaiza54a0802015-12-16 18:37:23 -08001318 // Build the a=candidate lines. We don't include ufrag and pwd in the
1319 // candidates in the SDP to avoid redundancy.
1320 BuildCandidate(candidates, false, message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001321
1322 // Use the transport_info to build the media level ice-ufrag and ice-pwd.
1323 if (transport_info) {
1324 // RFC 5245
1325 // ice-pwd-att = "ice-pwd" ":" password
1326 // ice-ufrag-att = "ice-ufrag" ":" ufrag
1327 // ice-ufrag
deadbeef3f7219b2015-12-28 15:17:14 -08001328 if (!transport_info->description.ice_ufrag.empty()) {
1329 InitAttrLine(kAttributeIceUfrag, &os);
1330 os << kSdpDelimiterColon << transport_info->description.ice_ufrag;
1331 AddLine(os.str(), message);
1332 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001333 // ice-pwd
deadbeef3f7219b2015-12-28 15:17:14 -08001334 if (!transport_info->description.ice_pwd.empty()) {
1335 InitAttrLine(kAttributeIcePwd, &os);
1336 os << kSdpDelimiterColon << transport_info->description.ice_pwd;
1337 AddLine(os.str(), message);
1338 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001339
1340 // draft-petithuguenin-mmusic-ice-attributes-level-03
1341 BuildIceOptions(transport_info->description.transport_options, message);
1342
1343 // RFC 4572
1344 // fingerprint-attribute =
1345 // "fingerprint" ":" hash-func SP fingerprint
1346 if (fp) {
1347 // Insert the fingerprint attribute.
1348 InitAttrLine(kAttributeFingerprint, &os);
1349 os << kSdpDelimiterColon
1350 << fp->algorithm << kSdpDelimiterSpace
1351 << fp->GetRfc4572Fingerprint();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001352 AddLine(os.str(), message);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001353
1354 // Inserting setup attribute.
1355 if (transport_info->description.connection_role !=
1356 cricket::CONNECTIONROLE_NONE) {
1357 // Making sure we are not using "passive" mode.
1358 cricket::ConnectionRole role =
1359 transport_info->description.connection_role;
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001360 std::string dtls_role_str;
1361 VERIFY(cricket::ConnectionRoleToString(role, &dtls_role_str));
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001362 InitAttrLine(kAttributeSetup, &os);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001363 os << kSdpDelimiterColon << dtls_role_str;
1364 AddLine(os.str(), message);
1365 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001366 }
1367 }
1368
1369 // RFC 3388
1370 // mid-attribute = "a=mid:" identification-tag
1371 // identification-tag = token
1372 // Use the content name as the mid identification-tag.
1373 InitAttrLine(kAttributeMid, &os);
1374 os << kSdpDelimiterColon << content_info->name;
1375 AddLine(os.str(), message);
1376
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001377 if (IsDtlsSctp(media_desc->protocol())) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001378 BuildSctpContentAttributes(message, sctp_port);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001379 } else if (IsRtp(media_desc->protocol())) {
deadbeef9d3584c2016-02-16 17:54:10 -08001380 BuildRtpContentAttributes(media_desc, media_type, unified_plan_sdp,
1381 message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001382 }
1383}
1384
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001385void BuildSctpContentAttributes(std::string* message, int sctp_port) {
wu@webrtc.org78187522013-10-07 23:32:02 +00001386 // draft-ietf-mmusic-sctp-sdp-04
1387 // a=sctpmap:sctpmap-number protocol [streams]
lally69f57602015-10-08 10:15:04 -07001388 // TODO(lally): switch this over to mmusic-sctp-sdp-12 (or later), with
1389 // 'a=sctp-port:'
wu@webrtc.org78187522013-10-07 23:32:02 +00001390 std::ostringstream os;
1391 InitAttrLine(kAttributeSctpmap, &os);
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001392 os << kSdpDelimiterColon << sctp_port << kSdpDelimiterSpace
wu@webrtc.org78187522013-10-07 23:32:02 +00001393 << kDefaultSctpmapProtocol << kSdpDelimiterSpace
Taylor Brandstetter1d7a6372016-08-24 13:15:27 -07001394 << cricket::kMaxSctpStreams;
wu@webrtc.org78187522013-10-07 23:32:02 +00001395 AddLine(os.str(), message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001396}
1397
deadbeef9d3584c2016-02-16 17:54:10 -08001398// If unified_plan_sdp is true, will use "a=msid".
1399void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
1400 const MediaType media_type,
1401 bool unified_plan_sdp,
1402 std::string* message) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001403 std::ostringstream os;
1404 // RFC 5285
1405 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1406 // The definitions MUST be either all session level or all media level. This
1407 // implementation uses all media level.
1408 for (size_t i = 0; i < media_desc->rtp_header_extensions().size(); ++i) {
1409 InitAttrLine(kAttributeExtmap, &os);
1410 os << kSdpDelimiterColon << media_desc->rtp_header_extensions()[i].id
1411 << kSdpDelimiterSpace << media_desc->rtp_header_extensions()[i].uri;
1412 AddLine(os.str(), message);
1413 }
1414
1415 // RFC 3264
1416 // a=sendrecv || a=sendonly || a=sendrecv || a=inactive
deadbeefc80741f2015-10-22 13:14:45 -07001417 switch (media_desc->direction()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001418 case cricket::MD_INACTIVE:
1419 InitAttrLine(kAttributeInactive, &os);
1420 break;
1421 case cricket::MD_SENDONLY:
1422 InitAttrLine(kAttributeSendOnly, &os);
1423 break;
1424 case cricket::MD_RECVONLY:
1425 InitAttrLine(kAttributeRecvOnly, &os);
1426 break;
1427 case cricket::MD_SENDRECV:
1428 default:
1429 InitAttrLine(kAttributeSendRecv, &os);
1430 break;
1431 }
1432 AddLine(os.str(), message);
1433
deadbeef9d3584c2016-02-16 17:54:10 -08001434 // draft-ietf-mmusic-msid-11
1435 // a=msid:<stream id> <track id>
1436 if (unified_plan_sdp && !media_desc->streams().empty()) {
1437 if (media_desc->streams().size() > 1u) {
1438 LOG(LS_WARNING) << "Trying to serialize unified plan SDP with more than "
1439 << "one track in a media section. Omitting 'a=msid'.";
1440 } else {
1441 auto track = media_desc->streams().begin();
1442 const std::string& stream_id = track->sync_label;
deadbeef9d3584c2016-02-16 17:54:10 -08001443 InitAttrLine(kAttributeMsid, &os);
1444 os << kSdpDelimiterColon << stream_id << kSdpDelimiterSpace << track->id;
1445 AddLine(os.str(), message);
1446 }
1447 }
1448
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001449 // RFC 5761
1450 // a=rtcp-mux
1451 if (media_desc->rtcp_mux()) {
1452 InitAttrLine(kAttributeRtcpMux, &os);
1453 AddLine(os.str(), message);
1454 }
1455
deadbeef13871492015-12-09 12:37:51 -08001456 // RFC 5506
1457 // a=rtcp-rsize
1458 if (media_desc->rtcp_reduced_size()) {
1459 InitAttrLine(kAttributeRtcpReducedSize, &os);
1460 AddLine(os.str(), message);
1461 }
1462
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001463 // RFC 4568
1464 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
1465 for (std::vector<CryptoParams>::const_iterator it =
1466 media_desc->cryptos().begin();
1467 it != media_desc->cryptos().end(); ++it) {
1468 InitAttrLine(kAttributeCrypto, &os);
1469 os << kSdpDelimiterColon << it->tag << " " << it->cipher_suite << " "
1470 << it->key_params;
1471 if (!it->session_params.empty()) {
1472 os << " " << it->session_params;
1473 }
1474 AddLine(os.str(), message);
1475 }
1476
1477 // RFC 4566
1478 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1479 // [/<encodingparameters>]
1480 BuildRtpMap(media_desc, media_type, message);
1481
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001482 for (StreamParamsVec::const_iterator track = media_desc->streams().begin();
1483 track != media_desc->streams().end(); ++track) {
1484 // Require that the track belongs to a media stream,
1485 // ie the sync_label is set. This extra check is necessary since the
1486 // MediaContentDescription always contains a streamparam with an ssrc even
1487 // if no track or media stream have been created.
1488 if (track->sync_label.empty()) continue;
1489
1490 // Build the ssrc-group lines.
1491 for (size_t i = 0; i < track->ssrc_groups.size(); ++i) {
1492 // RFC 5576
1493 // a=ssrc-group:<semantics> <ssrc-id> ...
1494 if (track->ssrc_groups[i].ssrcs.empty()) {
1495 continue;
1496 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001497 InitAttrLine(kAttributeSsrcGroup, &os);
1498 os << kSdpDelimiterColon << track->ssrc_groups[i].semantics;
Peter Boström0c4e06b2015-10-07 12:23:21 +02001499 std::vector<uint32_t>::const_iterator ssrc =
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001500 track->ssrc_groups[i].ssrcs.begin();
1501 for (; ssrc != track->ssrc_groups[i].ssrcs.end(); ++ssrc) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02001502 os << kSdpDelimiterSpace << rtc::ToString<uint32_t>(*ssrc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001503 }
1504 AddLine(os.str(), message);
1505 }
1506 // Build the ssrc lines for each ssrc.
1507 for (size_t i = 0; i < track->ssrcs.size(); ++i) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02001508 uint32_t ssrc = track->ssrcs[i];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001509 // RFC 5576
1510 // a=ssrc:<ssrc-id> cname:<value>
1511 AddSsrcLine(ssrc, kSsrcAttributeCname,
1512 track->cname, message);
1513
1514 // draft-alvestrand-mmusic-msid-00
1515 // a=ssrc:<ssrc-id> msid:identifier [appdata]
deadbeef9d3584c2016-02-16 17:54:10 -08001516 // The appdata consists of the "id" attribute of a MediaStreamTrack,
1517 // which corresponds to the "id" attribute of StreamParams.
1518 const std::string& stream_id = track->sync_label;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001519 InitAttrLine(kAttributeSsrc, &os);
1520 os << kSdpDelimiterColon << ssrc << kSdpDelimiterSpace
deadbeef9d3584c2016-02-16 17:54:10 -08001521 << kSsrcAttributeMsid << kSdpDelimiterColon << stream_id
1522 << kSdpDelimiterSpace << track->id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001523 AddLine(os.str(), message);
1524
deadbeef9d3584c2016-02-16 17:54:10 -08001525 // TODO(ronghuawu): Remove below code which is for backward
1526 // compatibility.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001527 // draft-alvestrand-rtcweb-mid-01
1528 // a=ssrc:<ssrc-id> mslabel:<value>
1529 // The label isn't yet defined.
1530 // a=ssrc:<ssrc-id> label:<value>
1531 AddSsrcLine(ssrc, kSsrcAttributeMslabel, track->sync_label, message);
1532 AddSsrcLine(ssrc, kSSrcAttributeLabel, track->id, message);
1533 }
1534 }
1535}
1536
1537void WriteFmtpHeader(int payload_type, std::ostringstream* os) {
1538 // fmtp header: a=fmtp:|payload_type| <parameters>
1539 // Add a=fmtp
1540 InitAttrLine(kAttributeFmtp, os);
1541 // Add :|payload_type|
1542 *os << kSdpDelimiterColon << payload_type;
1543}
1544
1545void WriteRtcpFbHeader(int payload_type, std::ostringstream* os) {
1546 // rtcp-fb header: a=rtcp-fb:|payload_type|
1547 // <parameters>/<ccm <ccm_parameters>>
1548 // Add a=rtcp-fb
1549 InitAttrLine(kAttributeRtcpFb, os);
1550 // Add :
1551 *os << kSdpDelimiterColon;
1552 if (payload_type == kWildcardPayloadType) {
1553 *os << "*";
1554 } else {
1555 *os << payload_type;
1556 }
1557}
1558
1559void WriteFmtpParameter(const std::string& parameter_name,
1560 const std::string& parameter_value,
1561 std::ostringstream* os) {
1562 // fmtp parameters: |parameter_name|=|parameter_value|
1563 *os << parameter_name << kSdpDelimiterEqual << parameter_value;
1564}
1565
1566void WriteFmtpParameters(const cricket::CodecParameterMap& parameters,
1567 std::ostringstream* os) {
1568 for (cricket::CodecParameterMap::const_iterator fmtp = parameters.begin();
1569 fmtp != parameters.end(); ++fmtp) {
hta62a216e2016-04-15 11:02:14 -07001570 // Parameters are a semicolon-separated list, no spaces.
1571 // The list is separated from the header by a space.
1572 if (fmtp == parameters.begin()) {
1573 *os << kSdpDelimiterSpace;
1574 } else {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001575 *os << kSdpDelimiterSemicolon;
1576 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001577 WriteFmtpParameter(fmtp->first, fmtp->second, os);
1578 }
1579}
1580
1581bool IsFmtpParam(const std::string& name) {
1582 const char* kFmtpParams[] = {
htaa6b99442016-04-12 10:29:17 -07001583 // TODO(hta): Split FMTP parameters apart from parameters in general.
1584 // FMTP parameters are codec specific, not generic.
1585 kCodecParamMinPTime,
1586 kCodecParamSPropStereo,
1587 kCodecParamStereo,
1588 kCodecParamUseInbandFec,
1589 kCodecParamUseDtx,
1590 kCodecParamStartBitrate,
1591 kCodecParamMaxBitrate,
1592 kCodecParamMinBitrate,
1593 kCodecParamMaxQuantization,
1594 kCodecParamSctpProtocol,
1595 kCodecParamSctpStreams,
1596 kCodecParamMaxAverageBitrate,
1597 kCodecParamMaxPlaybackRate,
1598 kCodecParamAssociatedPayloadType,
1599 cricket::kH264FmtpPacketizationMode,
1600 cricket::kH264FmtpLevelAsymmetryAllowed,
1601 cricket::kH264FmtpProfileLevelId};
tfarina5237aaf2015-11-10 23:44:30 -08001602 for (size_t i = 0; i < arraysize(kFmtpParams); ++i) {
htaa6b99442016-04-12 10:29:17 -07001603 if (name.compare(kFmtpParams[i]) == 0) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001604 return true;
1605 }
1606 }
1607 return false;
1608}
1609
1610// Retreives fmtp parameters from |params|, which may contain other parameters
1611// as well, and puts them in |fmtp_parameters|.
1612void GetFmtpParams(const cricket::CodecParameterMap& params,
1613 cricket::CodecParameterMap* fmtp_parameters) {
1614 for (cricket::CodecParameterMap::const_iterator iter = params.begin();
1615 iter != params.end(); ++iter) {
1616 if (IsFmtpParam(iter->first)) {
1617 (*fmtp_parameters)[iter->first] = iter->second;
1618 }
1619 }
1620}
1621
1622template <class T>
1623void AddFmtpLine(const T& codec, std::string* message) {
1624 cricket::CodecParameterMap fmtp_parameters;
1625 GetFmtpParams(codec.params, &fmtp_parameters);
1626 if (fmtp_parameters.empty()) {
1627 // No need to add an fmtp if it will have no (optional) parameters.
1628 return;
1629 }
1630 std::ostringstream os;
1631 WriteFmtpHeader(codec.id, &os);
1632 WriteFmtpParameters(fmtp_parameters, &os);
1633 AddLine(os.str(), message);
1634 return;
1635}
1636
1637template <class T>
1638void AddRtcpFbLines(const T& codec, std::string* message) {
1639 for (std::vector<cricket::FeedbackParam>::const_iterator iter =
1640 codec.feedback_params.params().begin();
1641 iter != codec.feedback_params.params().end(); ++iter) {
1642 std::ostringstream os;
1643 WriteRtcpFbHeader(codec.id, &os);
1644 os << " " << iter->id();
1645 if (!iter->param().empty()) {
1646 os << " " << iter->param();
1647 }
1648 AddLine(os.str(), message);
1649 }
1650}
1651
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001652bool AddSctpDataCodec(DataContentDescription* media_desc,
1653 int sctp_port) {
solenberg9fa49752016-10-08 13:02:44 -07001654 for (const auto& codec : media_desc->codecs()) {
1655 if (cricket::CodecNamesEq(codec.name, cricket::kGoogleSctpDataCodecName)) {
1656 return ParseFailed("",
1657 "Can't have multiple sctp port attributes.",
1658 NULL);
1659 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001660 }
1661 // Add the SCTP Port number as a pseudo-codec "port" parameter
solenberg9fa49752016-10-08 13:02:44 -07001662 cricket::DataCodec codec_port(cricket::kGoogleSctpDataCodecPlType,
deadbeef67cf2c12016-04-13 10:07:16 -07001663 cricket::kGoogleSctpDataCodecName);
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001664 codec_port.SetParam(cricket::kCodecParamPort, sctp_port);
1665 LOG(INFO) << "AddSctpDataCodec: Got SCTP Port Number "
1666 << sctp_port;
1667 media_desc->AddCodec(codec_port);
1668 return true;
1669}
1670
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001671bool GetMinValue(const std::vector<int>& values, int* value) {
1672 if (values.empty()) {
1673 return false;
1674 }
1675 std::vector<int>::const_iterator found =
1676 std::min_element(values.begin(), values.end());
1677 *value = *found;
1678 return true;
1679}
1680
1681bool GetParameter(const std::string& name,
1682 const cricket::CodecParameterMap& params, int* value) {
1683 std::map<std::string, std::string>::const_iterator found =
1684 params.find(name);
1685 if (found == params.end()) {
1686 return false;
1687 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001688 if (!rtc::FromString(found->second, value)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001689 return false;
1690 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001691 return true;
1692}
1693
1694void BuildRtpMap(const MediaContentDescription* media_desc,
1695 const MediaType media_type,
1696 std::string* message) {
1697 ASSERT(message != NULL);
1698 ASSERT(media_desc != NULL);
1699 std::ostringstream os;
1700 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
1701 const VideoContentDescription* video_desc =
1702 static_cast<const VideoContentDescription*>(media_desc);
1703 for (std::vector<cricket::VideoCodec>::const_iterator it =
1704 video_desc->codecs().begin();
1705 it != video_desc->codecs().end(); ++it) {
1706 // RFC 4566
1707 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1708 // [/<encodingparameters>]
1709 if (it->id != kWildcardPayloadType) {
1710 InitAttrLine(kAttributeRtpmap, &os);
1711 os << kSdpDelimiterColon << it->id << " " << it->name
1712 << "/" << kDefaultVideoClockrate;
1713 AddLine(os.str(), message);
1714 }
1715 AddRtcpFbLines(*it, message);
1716 AddFmtpLine(*it, message);
1717 }
1718 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
1719 const AudioContentDescription* audio_desc =
1720 static_cast<const AudioContentDescription*>(media_desc);
1721 std::vector<int> ptimes;
1722 std::vector<int> maxptimes;
1723 int max_minptime = 0;
1724 for (std::vector<cricket::AudioCodec>::const_iterator it =
1725 audio_desc->codecs().begin();
1726 it != audio_desc->codecs().end(); ++it) {
1727 ASSERT(!it->name.empty());
1728 // RFC 4566
1729 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1730 // [/<encodingparameters>]
1731 InitAttrLine(kAttributeRtpmap, &os);
1732 os << kSdpDelimiterColon << it->id << " ";
1733 os << it->name << "/" << it->clockrate;
1734 if (it->channels != 1) {
1735 os << "/" << it->channels;
1736 }
1737 AddLine(os.str(), message);
1738 AddRtcpFbLines(*it, message);
1739 AddFmtpLine(*it, message);
1740 int minptime = 0;
1741 if (GetParameter(kCodecParamMinPTime, it->params, &minptime)) {
1742 max_minptime = std::max(minptime, max_minptime);
1743 }
1744 int ptime;
1745 if (GetParameter(kCodecParamPTime, it->params, &ptime)) {
1746 ptimes.push_back(ptime);
1747 }
1748 int maxptime;
1749 if (GetParameter(kCodecParamMaxPTime, it->params, &maxptime)) {
1750 maxptimes.push_back(maxptime);
1751 }
1752 }
1753 // Populate the maxptime attribute with the smallest maxptime of all codecs
1754 // under the same m-line.
1755 int min_maxptime = INT_MAX;
1756 if (GetMinValue(maxptimes, &min_maxptime)) {
1757 AddAttributeLine(kCodecParamMaxPTime, min_maxptime, message);
1758 }
1759 ASSERT(min_maxptime > max_minptime);
1760 // Populate the ptime attribute with the smallest ptime or the largest
1761 // minptime, whichever is the largest, for all codecs under the same m-line.
1762 int ptime = INT_MAX;
1763 if (GetMinValue(ptimes, &ptime)) {
1764 ptime = std::min(ptime, min_maxptime);
1765 ptime = std::max(ptime, max_minptime);
1766 AddAttributeLine(kCodecParamPTime, ptime, message);
1767 }
1768 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
1769 const DataContentDescription* data_desc =
1770 static_cast<const DataContentDescription*>(media_desc);
1771 for (std::vector<cricket::DataCodec>::const_iterator it =
1772 data_desc->codecs().begin();
1773 it != data_desc->codecs().end(); ++it) {
1774 // RFC 4566
1775 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1776 // [/<encodingparameters>]
1777 InitAttrLine(kAttributeRtpmap, &os);
1778 os << kSdpDelimiterColon << it->id << " "
1779 << it->name << "/" << it->clockrate;
1780 AddLine(os.str(), message);
1781 }
1782 }
1783}
1784
1785void BuildCandidate(const std::vector<Candidate>& candidates,
honghaiza54a0802015-12-16 18:37:23 -08001786 bool include_ufrag,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001787 std::string* message) {
1788 std::ostringstream os;
1789
1790 for (std::vector<Candidate>::const_iterator it = candidates.begin();
1791 it != candidates.end(); ++it) {
1792 // RFC 5245
1793 // a=candidate:<foundation> <component-id> <transport> <priority>
1794 // <connection-address> <port> typ <candidate-types>
1795 // [raddr <connection-address>] [rport <port>]
1796 // *(SP extension-att-name SP extension-att-value)
1797 std::string type;
1798 // Map the cricket candidate type to "host" / "srflx" / "prflx" / "relay"
1799 if (it->type() == cricket::LOCAL_PORT_TYPE) {
1800 type = kCandidateHost;
1801 } else if (it->type() == cricket::STUN_PORT_TYPE) {
1802 type = kCandidateSrflx;
1803 } else if (it->type() == cricket::RELAY_PORT_TYPE) {
1804 type = kCandidateRelay;
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001805 } else if (it->type() == cricket::PRFLX_PORT_TYPE) {
1806 type = kCandidatePrflx;
1807 // Peer reflexive candidate may be signaled for being removed.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001808 } else {
1809 ASSERT(false);
Peter Thatcher019087f2015-04-28 09:06:26 -07001810 // Never write out candidates if we don't know the type.
1811 continue;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001812 }
1813
1814 InitAttrLine(kAttributeCandidate, &os);
1815 os << kSdpDelimiterColon
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001816 << it->foundation() << " "
1817 << it->component() << " "
1818 << it->protocol() << " "
1819 << it->priority() << " "
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001820 << it->address().ipaddr().ToString() << " "
1821 << it->address().PortAsString() << " "
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001822 << kAttributeCandidateTyp << " "
1823 << type << " ";
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001824
1825 // Related address
1826 if (!it->related_address().IsNil()) {
1827 os << kAttributeCandidateRaddr << " "
1828 << it->related_address().ipaddr().ToString() << " "
1829 << kAttributeCandidateRport << " "
1830 << it->related_address().PortAsString() << " ";
1831 }
1832
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001833 if (it->protocol() == cricket::TCP_PROTOCOL_NAME) {
mallinath@webrtc.orge999bd02014-08-13 06:05:55 +00001834 os << kTcpCandidateType << " " << it->tcptype() << " ";
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001835 }
1836
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001837 // Extensions
1838 os << kAttributeCandidateGeneration << " " << it->generation();
honghaiza54a0802015-12-16 18:37:23 -08001839 if (include_ufrag && !it->username().empty()) {
1840 os << " " << kAttributeCandidateUfrag << " " << it->username();
1841 }
honghaiza0c44ea2016-03-23 16:07:48 -07001842 if (it->network_id() > 0) {
1843 os << " " << kAttributeCandidateNetworkId << " " << it->network_id();
1844 }
honghaize1a0c942016-02-16 14:54:56 -08001845 if (it->network_cost() > 0) {
1846 os << " " << kAttributeCandidateNetworkCost << " " << it->network_cost();
1847 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001848
1849 AddLine(os.str(), message);
1850 }
1851}
1852
1853void BuildIceOptions(const std::vector<std::string>& transport_options,
1854 std::string* message) {
1855 if (!transport_options.empty()) {
1856 std::ostringstream os;
1857 InitAttrLine(kAttributeIceOption, &os);
1858 os << kSdpDelimiterColon << transport_options[0];
1859 for (size_t i = 1; i < transport_options.size(); ++i) {
1860 os << kSdpDelimiterSpace << transport_options[i];
1861 }
1862 AddLine(os.str(), message);
1863 }
1864}
1865
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001866bool IsRtp(const std::string& protocol) {
1867 return protocol.empty() ||
1868 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
1869}
1870
1871bool IsDtlsSctp(const std::string& protocol) {
1872 // This intentionally excludes "SCTP" and "SCTP/DTLS".
lally@webrtc.orga7470932015-02-24 20:19:21 +00001873 return protocol.find(cricket::kMediaProtocolDtlsSctp) != std::string::npos;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001874}
1875
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001876bool ParseSessionDescription(const std::string& message, size_t* pos,
1877 std::string* session_id,
1878 std::string* session_version,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001879 TransportDescription* session_td,
1880 RtpHeaderExtensions* session_extmaps,
1881 cricket::SessionDescription* desc,
1882 SdpParseError* error) {
1883 std::string line;
1884
deadbeefc80741f2015-10-22 13:14:45 -07001885 desc->set_msid_supported(false);
1886
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001887 // RFC 4566
1888 // v= (protocol version)
1889 if (!GetLineWithType(message, pos, &line, kLineTypeVersion)) {
1890 return ParseFailedExpectLine(message, *pos, kLineTypeVersion,
1891 std::string(), error);
1892 }
1893 // RFC 4566
1894 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
1895 // <unicast-address>
1896 if (!GetLineWithType(message, pos, &line, kLineTypeOrigin)) {
1897 return ParseFailedExpectLine(message, *pos, kLineTypeOrigin,
1898 std::string(), error);
1899 }
1900 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001901 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001902 kSdpDelimiterSpace, &fields);
1903 const size_t expected_fields = 6;
1904 if (fields.size() != expected_fields) {
1905 return ParseFailedExpectFieldNum(line, expected_fields, error);
1906 }
1907 *session_id = fields[1];
1908 *session_version = fields[2];
1909
1910 // RFC 4566
1911 // s= (session name)
1912 if (!GetLineWithType(message, pos, &line, kLineTypeSessionName)) {
1913 return ParseFailedExpectLine(message, *pos, kLineTypeSessionName,
1914 std::string(), error);
1915 }
1916
1917 // Optional lines
1918 // Those are the optional lines, so shouldn't return false if not present.
1919 // RFC 4566
1920 // i=* (session information)
1921 GetLineWithType(message, pos, &line, kLineTypeSessionInfo);
1922
1923 // RFC 4566
1924 // u=* (URI of description)
1925 GetLineWithType(message, pos, &line, kLineTypeSessionUri);
1926
1927 // RFC 4566
1928 // e=* (email address)
1929 GetLineWithType(message, pos, &line, kLineTypeSessionEmail);
1930
1931 // RFC 4566
1932 // p=* (phone number)
1933 GetLineWithType(message, pos, &line, kLineTypeSessionPhone);
1934
1935 // RFC 4566
1936 // c=* (connection information -- not required if included in
1937 // all media)
1938 GetLineWithType(message, pos, &line, kLineTypeConnection);
1939
1940 // RFC 4566
1941 // b=* (zero or more bandwidth information lines)
1942 while (GetLineWithType(message, pos, &line, kLineTypeSessionBandwidth)) {
1943 // By pass zero or more b lines.
1944 }
1945
1946 // RFC 4566
1947 // One or more time descriptions ("t=" and "r=" lines; see below)
1948 // t= (time the session is active)
1949 // r=* (zero or more repeat times)
1950 // Ensure there's at least one time description
1951 if (!GetLineWithType(message, pos, &line, kLineTypeTiming)) {
1952 return ParseFailedExpectLine(message, *pos, kLineTypeTiming, std::string(),
1953 error);
1954 }
1955
1956 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
1957 // By pass zero or more r lines.
1958 }
1959
1960 // Go through the rest of the time descriptions
1961 while (GetLineWithType(message, pos, &line, kLineTypeTiming)) {
1962 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
1963 // By pass zero or more r lines.
1964 }
1965 }
1966
1967 // RFC 4566
1968 // z=* (time zone adjustments)
1969 GetLineWithType(message, pos, &line, kLineTypeTimeZone);
1970
1971 // RFC 4566
1972 // k=* (encryption key)
1973 GetLineWithType(message, pos, &line, kLineTypeEncryptionKey);
1974
1975 // RFC 4566
1976 // a=* (zero or more session attribute lines)
1977 while (GetLineWithType(message, pos, &line, kLineTypeAttributes)) {
1978 if (HasAttribute(line, kAttributeGroup)) {
1979 if (!ParseGroupAttribute(line, desc, error)) {
1980 return false;
1981 }
1982 } else if (HasAttribute(line, kAttributeIceUfrag)) {
1983 if (!GetValue(line, kAttributeIceUfrag,
1984 &(session_td->ice_ufrag), error)) {
1985 return false;
1986 }
1987 } else if (HasAttribute(line, kAttributeIcePwd)) {
1988 if (!GetValue(line, kAttributeIcePwd, &(session_td->ice_pwd), error)) {
1989 return false;
1990 }
1991 } else if (HasAttribute(line, kAttributeIceLite)) {
1992 session_td->ice_mode = cricket::ICEMODE_LITE;
1993 } else if (HasAttribute(line, kAttributeIceOption)) {
1994 if (!ParseIceOptions(line, &(session_td->transport_options), error)) {
1995 return false;
1996 }
1997 } else if (HasAttribute(line, kAttributeFingerprint)) {
1998 if (session_td->identity_fingerprint.get()) {
1999 return ParseFailed(
2000 line,
2001 "Can't have multiple fingerprint attributes at the same level.",
2002 error);
2003 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002004 rtc::SSLFingerprint* fingerprint = NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002005 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
2006 return false;
2007 }
2008 session_td->identity_fingerprint.reset(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002009 } else if (HasAttribute(line, kAttributeSetup)) {
2010 if (!ParseDtlsSetup(line, &(session_td->connection_role), error)) {
2011 return false;
2012 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002013 } else if (HasAttribute(line, kAttributeMsidSemantics)) {
2014 std::string semantics;
2015 if (!GetValue(line, kAttributeMsidSemantics, &semantics, error)) {
2016 return false;
2017 }
deadbeefc80741f2015-10-22 13:14:45 -07002018 desc->set_msid_supported(
2019 CaseInsensitiveFind(semantics, kMediaStreamSemantic));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002020 } else if (HasAttribute(line, kAttributeExtmap)) {
isheriff6f8d6862016-05-26 11:24:55 -07002021 RtpExtension extmap;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002022 if (!ParseExtmap(line, &extmap, error)) {
2023 return false;
2024 }
2025 session_extmaps->push_back(extmap);
2026 }
2027 }
2028
2029 return true;
2030}
2031
2032bool ParseGroupAttribute(const std::string& line,
2033 cricket::SessionDescription* desc,
2034 SdpParseError* error) {
2035 ASSERT(desc != NULL);
2036
2037 // RFC 5888 and draft-holmberg-mmusic-sdp-bundle-negotiation-00
2038 // a=group:BUNDLE video voice
2039 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002040 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002041 kSdpDelimiterSpace, &fields);
2042 std::string semantics;
2043 if (!GetValue(fields[0], kAttributeGroup, &semantics, error)) {
2044 return false;
2045 }
2046 cricket::ContentGroup group(semantics);
2047 for (size_t i = 1; i < fields.size(); ++i) {
2048 group.AddContentName(fields[i]);
2049 }
2050 desc->AddGroup(group);
2051 return true;
2052}
2053
2054static bool ParseFingerprintAttribute(const std::string& line,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002055 rtc::SSLFingerprint** fingerprint,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002056 SdpParseError* error) {
2057 if (!IsLineType(line, kLineTypeAttributes) ||
2058 !HasAttribute(line, kAttributeFingerprint)) {
2059 return ParseFailedExpectLine(line, 0, kLineTypeAttributes,
2060 kAttributeFingerprint, error);
2061 }
2062
2063 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002064 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002065 kSdpDelimiterSpace, &fields);
2066 const size_t expected_fields = 2;
2067 if (fields.size() != expected_fields) {
2068 return ParseFailedExpectFieldNum(line, expected_fields, error);
2069 }
2070
2071 // The first field here is "fingerprint:<hash>.
2072 std::string algorithm;
2073 if (!GetValue(fields[0], kAttributeFingerprint, &algorithm, error)) {
2074 return false;
2075 }
2076
2077 // Downcase the algorithm. Note that we don't need to downcase the
2078 // fingerprint because hex_decode can handle upper-case.
2079 std::transform(algorithm.begin(), algorithm.end(), algorithm.begin(),
2080 ::tolower);
2081
2082 // The second field is the digest value. De-hexify it.
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002083 *fingerprint = rtc::SSLFingerprint::CreateFromRfc4572(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002084 algorithm, fields[1]);
2085 if (!*fingerprint) {
2086 return ParseFailed(line,
2087 "Failed to create fingerprint from the digest.",
2088 error);
2089 }
2090
2091 return true;
2092}
2093
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002094static bool ParseDtlsSetup(const std::string& line,
2095 cricket::ConnectionRole* role,
2096 SdpParseError* error) {
2097 // setup-attr = "a=setup:" role
2098 // role = "active" / "passive" / "actpass" / "holdconn"
2099 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002100 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColon, &fields);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002101 const size_t expected_fields = 2;
2102 if (fields.size() != expected_fields) {
2103 return ParseFailedExpectFieldNum(line, expected_fields, error);
2104 }
2105 std::string role_str = fields[1];
2106 if (!cricket::StringToConnectionRole(role_str, role)) {
2107 return ParseFailed(line, "Invalid attribute value.", error);
2108 }
2109 return true;
2110}
2111
deadbeef9d3584c2016-02-16 17:54:10 -08002112static bool ParseMsidAttribute(const std::string& line,
2113 std::string* stream_id,
2114 std::string* track_id,
2115 SdpParseError* error) {
2116 // draft-ietf-mmusic-msid-11
2117 // a=msid:<stream id> <track id>
2118 // msid-value = msid-id [ SP msid-appdata ]
2119 // msid-id = 1*64token-char ; see RFC 4566
2120 // msid-appdata = 1*64token-char ; see RFC 4566
2121 std::string field1;
2122 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
2123 &field1, track_id)) {
2124 const size_t expected_fields = 2;
2125 return ParseFailedExpectFieldNum(line, expected_fields, error);
2126 }
2127
2128 // msid:<msid-id>
2129 if (!GetValue(field1, kAttributeMsid, stream_id, error)) {
2130 return false;
2131 }
2132 return true;
2133}
2134
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002135// RFC 3551
2136// PT encoding media type clock rate channels
2137// name (Hz)
2138// 0 PCMU A 8,000 1
2139// 1 reserved A
2140// 2 reserved A
2141// 3 GSM A 8,000 1
2142// 4 G723 A 8,000 1
2143// 5 DVI4 A 8,000 1
2144// 6 DVI4 A 16,000 1
2145// 7 LPC A 8,000 1
2146// 8 PCMA A 8,000 1
2147// 9 G722 A 8,000 1
2148// 10 L16 A 44,100 2
2149// 11 L16 A 44,100 1
2150// 12 QCELP A 8,000 1
2151// 13 CN A 8,000 1
2152// 14 MPA A 90,000 (see text)
2153// 15 G728 A 8,000 1
2154// 16 DVI4 A 11,025 1
2155// 17 DVI4 A 22,050 1
2156// 18 G729 A 8,000 1
2157struct StaticPayloadAudioCodec {
2158 const char* name;
2159 int clockrate;
Peter Kasting69558702016-01-12 16:26:35 -08002160 size_t channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002161};
2162static const StaticPayloadAudioCodec kStaticPayloadAudioCodecs[] = {
2163 { "PCMU", 8000, 1 },
2164 { "reserved", 0, 0 },
2165 { "reserved", 0, 0 },
2166 { "GSM", 8000, 1 },
2167 { "G723", 8000, 1 },
2168 { "DVI4", 8000, 1 },
2169 { "DVI4", 16000, 1 },
2170 { "LPC", 8000, 1 },
2171 { "PCMA", 8000, 1 },
2172 { "G722", 8000, 1 },
2173 { "L16", 44100, 2 },
2174 { "L16", 44100, 1 },
2175 { "QCELP", 8000, 1 },
2176 { "CN", 8000, 1 },
2177 { "MPA", 90000, 1 },
2178 { "G728", 8000, 1 },
2179 { "DVI4", 11025, 1 },
2180 { "DVI4", 22050, 1 },
2181 { "G729", 8000, 1 },
2182};
2183
2184void MaybeCreateStaticPayloadAudioCodecs(
2185 const std::vector<int>& fmts, AudioContentDescription* media_desc) {
2186 if (!media_desc) {
2187 return;
2188 }
deadbeef67cf2c12016-04-13 10:07:16 -07002189 RTC_DCHECK(media_desc->codecs().empty());
solenberg9fa49752016-10-08 13:02:44 -07002190 for (int payload_type : fmts) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002191 if (!media_desc->HasCodec(payload_type) &&
2192 payload_type >= 0 &&
kjellander3e33bfe2016-06-20 07:04:09 -07002193 static_cast<uint32_t>(payload_type) <
2194 arraysize(kStaticPayloadAudioCodecs)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002195 std::string encoding_name = kStaticPayloadAudioCodecs[payload_type].name;
2196 int clock_rate = kStaticPayloadAudioCodecs[payload_type].clockrate;
Peter Kasting69558702016-01-12 16:26:35 -08002197 size_t channels = kStaticPayloadAudioCodecs[payload_type].channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002198 media_desc->AddCodec(cricket::AudioCodec(payload_type, encoding_name,
deadbeef67cf2c12016-04-13 10:07:16 -07002199 clock_rate, 0, channels));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002200 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002201 }
2202}
2203
2204template <class C>
2205static C* ParseContentDescription(const std::string& message,
2206 const MediaType media_type,
2207 int mline_index,
2208 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -07002209 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002210 size_t* pos,
2211 std::string* content_name,
2212 TransportDescription* transport,
2213 std::vector<JsepIceCandidate*>* candidates,
2214 webrtc::SdpParseError* error) {
2215 C* media_desc = new C();
2216 switch (media_type) {
2217 case cricket::MEDIA_TYPE_AUDIO:
2218 *content_name = cricket::CN_AUDIO;
2219 break;
2220 case cricket::MEDIA_TYPE_VIDEO:
2221 *content_name = cricket::CN_VIDEO;
2222 break;
2223 case cricket::MEDIA_TYPE_DATA:
2224 *content_name = cricket::CN_DATA;
2225 break;
2226 default:
2227 ASSERT(false);
2228 break;
2229 }
deadbeef67cf2c12016-04-13 10:07:16 -07002230 if (!ParseContent(message, media_type, mline_index, protocol, payload_types,
2231 pos, content_name, media_desc, transport, candidates,
2232 error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002233 delete media_desc;
2234 return NULL;
2235 }
2236 // Sort the codecs according to the m-line fmt list.
deadbeef67cf2c12016-04-13 10:07:16 -07002237 std::unordered_map<int, int> payload_type_preferences;
2238 // "size + 1" so that the lowest preference payload type has a preference of
2239 // 1, which is greater than the default (0) for payload types not in the fmt
2240 // list.
2241 int preference = static_cast<int>(payload_types.size() + 1);
2242 for (int pt : payload_types) {
2243 payload_type_preferences[pt] = preference--;
2244 }
2245 std::vector<typename C::CodecType> codecs = media_desc->codecs();
2246 std::sort(codecs.begin(), codecs.end(), [&payload_type_preferences](
2247 const typename C::CodecType& a,
2248 const typename C::CodecType& b) {
2249 return payload_type_preferences[a.id] > payload_type_preferences[b.id];
2250 });
2251 media_desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002252 return media_desc;
2253}
2254
2255bool ParseMediaDescription(const std::string& message,
2256 const TransportDescription& session_td,
2257 const RtpHeaderExtensions& session_extmaps,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002258 size_t* pos,
2259 cricket::SessionDescription* desc,
2260 std::vector<JsepIceCandidate*>* candidates,
2261 SdpParseError* error) {
2262 ASSERT(desc != NULL);
2263 std::string line;
2264 int mline_index = -1;
2265
2266 // Zero or more media descriptions
2267 // RFC 4566
2268 // m=<media> <port> <proto> <fmt>
2269 while (GetLineWithType(message, pos, &line, kLineTypeMedia)) {
2270 ++mline_index;
2271
2272 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002273 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002274 kSdpDelimiterSpace, &fields);
2275 const size_t expected_min_fields = 4;
2276 if (fields.size() < expected_min_fields) {
2277 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2278 }
2279 bool rejected = false;
2280 // RFC 3264
2281 // To reject an offered stream, the port number in the corresponding stream
2282 // in the answer MUST be set to zero.
2283 if (fields[1] == kMediaPortRejected) {
2284 rejected = true;
2285 }
2286
2287 std::string protocol = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002288
2289 // <fmt>
deadbeef67cf2c12016-04-13 10:07:16 -07002290 std::vector<int> payload_types;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002291 if (IsRtp(protocol)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002292 for (size_t j = 3 ; j < fields.size(); ++j) {
2293 // TODO(wu): Remove when below bug is fixed.
2294 // https://bugzilla.mozilla.org/show_bug.cgi?id=996329
pbosbb36fdf2015-07-09 07:48:14 -07002295 if (fields[j].empty() && j == fields.size() - 1) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002296 continue;
2297 }
wu@webrtc.org36eda7c2014-04-15 20:37:30 +00002298
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002299 int pl = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00002300 if (!GetPayloadTypeFromString(line, fields[j], &pl, error)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002301 return false;
2302 }
deadbeef67cf2c12016-04-13 10:07:16 -07002303 payload_types.push_back(pl);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002304 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002305 }
2306
2307 // Make a temporary TransportDescription based on |session_td|.
2308 // Some of this gets overwritten by ParseContent.
deadbeef46eed762016-01-28 13:24:37 -08002309 TransportDescription transport(
2310 session_td.transport_options, session_td.ice_ufrag, session_td.ice_pwd,
2311 session_td.ice_mode, session_td.connection_role,
2312 session_td.identity_fingerprint.get());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002313
kwibergd1fe2812016-04-27 06:47:29 -07002314 std::unique_ptr<MediaContentDescription> content;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002315 std::string content_name;
2316 if (HasAttribute(line, kMediaTypeVideo)) {
2317 content.reset(ParseContentDescription<VideoContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002318 message, cricket::MEDIA_TYPE_VIDEO, mline_index, protocol,
2319 payload_types, pos, &content_name, &transport, candidates, error));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002320 } else if (HasAttribute(line, kMediaTypeAudio)) {
2321 content.reset(ParseContentDescription<AudioContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002322 message, cricket::MEDIA_TYPE_AUDIO, mline_index, protocol,
2323 payload_types, pos, &content_name, &transport, candidates, error));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002324 } else if (HasAttribute(line, kMediaTypeData)) {
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002325 DataContentDescription* data_desc =
wu@webrtc.org78187522013-10-07 23:32:02 +00002326 ParseContentDescription<DataContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002327 message, cricket::MEDIA_TYPE_DATA, mline_index, protocol,
2328 payload_types, pos, &content_name, &transport, candidates, error);
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002329 content.reset(data_desc);
wu@webrtc.org78187522013-10-07 23:32:02 +00002330
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002331 int p;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002332 if (data_desc && IsDtlsSctp(protocol) && rtc::FromString(fields[3], &p)) {
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002333 if (!AddSctpDataCodec(data_desc, p))
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002334 return false;
wu@webrtc.org78187522013-10-07 23:32:02 +00002335 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002336 } else {
2337 LOG(LS_WARNING) << "Unsupported media type: " << line;
2338 continue;
2339 }
2340 if (!content.get()) {
2341 // ParseContentDescription returns NULL if failed.
2342 return false;
2343 }
2344
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002345 if (IsRtp(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002346 // Set the extmap.
2347 if (!session_extmaps.empty() &&
2348 !content->rtp_header_extensions().empty()) {
2349 return ParseFailed("",
2350 "The a=extmap MUST be either all session level or "
2351 "all media level.",
2352 error);
2353 }
2354 for (size_t i = 0; i < session_extmaps.size(); ++i) {
2355 content->AddRtpHeaderExtension(session_extmaps[i]);
2356 }
2357 }
2358 content->set_protocol(protocol);
2359 desc->AddContent(content_name,
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002360 IsDtlsSctp(protocol) ? cricket::NS_JINGLE_DRAFT_SCTP :
2361 cricket::NS_JINGLE_RTP,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002362 rejected,
2363 content.release());
2364 // Create TransportInfo with the media level "ice-pwd" and "ice-ufrag".
2365 TransportInfo transport_info(content_name, transport);
2366
2367 if (!desc->AddTransportInfo(transport_info)) {
2368 std::ostringstream description;
2369 description << "Failed to AddTransportInfo with content name: "
2370 << content_name;
2371 return ParseFailed("", description.str(), error);
2372 }
2373 }
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00002374
2375 size_t end_of_message = message.size();
2376 if (mline_index == -1 && *pos != end_of_message) {
2377 ParseFailed(message, *pos, "Expects m line.", error);
2378 return false;
2379 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002380 return true;
2381}
2382
2383bool VerifyCodec(const cricket::Codec& codec) {
2384 // Codec has not been populated correctly unless the name has been set. This
2385 // can happen if an SDP has an fmtp or rtcp-fb with a payload type but doesn't
2386 // have a corresponding "rtpmap" line.
2387 cricket::Codec default_codec;
2388 return default_codec.name != codec.name;
2389}
2390
2391bool VerifyAudioCodecs(const AudioContentDescription* audio_desc) {
2392 const std::vector<cricket::AudioCodec>& codecs = audio_desc->codecs();
2393 for (std::vector<cricket::AudioCodec>::const_iterator iter = codecs.begin();
2394 iter != codecs.end(); ++iter) {
2395 if (!VerifyCodec(*iter)) {
2396 return false;
2397 }
2398 }
2399 return true;
2400}
2401
2402bool VerifyVideoCodecs(const VideoContentDescription* video_desc) {
2403 const std::vector<cricket::VideoCodec>& codecs = video_desc->codecs();
2404 for (std::vector<cricket::VideoCodec>::const_iterator iter = codecs.begin();
2405 iter != codecs.end(); ++iter) {
2406 if (!VerifyCodec(*iter)) {
2407 return false;
2408 }
2409 }
2410 return true;
2411}
2412
2413void AddParameters(const cricket::CodecParameterMap& parameters,
2414 cricket::Codec* codec) {
2415 for (cricket::CodecParameterMap::const_iterator iter =
2416 parameters.begin(); iter != parameters.end(); ++iter) {
2417 codec->SetParam(iter->first, iter->second);
2418 }
2419}
2420
2421void AddFeedbackParameter(const cricket::FeedbackParam& feedback_param,
2422 cricket::Codec* codec) {
2423 codec->AddFeedbackParam(feedback_param);
2424}
2425
2426void AddFeedbackParameters(const cricket::FeedbackParams& feedback_params,
2427 cricket::Codec* codec) {
2428 for (std::vector<cricket::FeedbackParam>::const_iterator iter =
2429 feedback_params.params().begin();
2430 iter != feedback_params.params().end(); ++iter) {
2431 codec->AddFeedbackParam(*iter);
2432 }
2433}
2434
2435// Gets the current codec setting associated with |payload_type|. If there
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002436// is no Codec associated with that payload type it returns an empty codec
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002437// with that payload type.
2438template <class T>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002439T GetCodecWithPayloadType(const std::vector<T>& codecs, int payload_type) {
2440 T ret_val;
2441 if (!FindCodecById(codecs, payload_type, &ret_val)) {
2442 ret_val.id = payload_type;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002443 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002444 return ret_val;
2445}
2446
2447// Updates or creates a new codec entry in the audio description.
2448template <class T, class U>
2449void AddOrReplaceCodec(MediaContentDescription* content_desc, const U& codec) {
2450 T* desc = static_cast<T*>(content_desc);
2451 std::vector<U> codecs = desc->codecs();
2452 bool found = false;
2453
2454 typename std::vector<U>::iterator iter;
2455 for (iter = codecs.begin(); iter != codecs.end(); ++iter) {
2456 if (iter->id == codec.id) {
2457 *iter = codec;
2458 found = true;
2459 break;
2460 }
2461 }
2462 if (!found) {
2463 desc->AddCodec(codec);
2464 return;
2465 }
2466 desc->set_codecs(codecs);
2467}
2468
2469// Adds or updates existing codec corresponding to |payload_type| according
2470// to |parameters|.
2471template <class T, class U>
2472void UpdateCodec(MediaContentDescription* content_desc, int payload_type,
2473 const cricket::CodecParameterMap& parameters) {
2474 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002475 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2476 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002477 AddParameters(parameters, &new_codec);
2478 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2479}
2480
2481// Adds or updates existing codec corresponding to |payload_type| according
2482// to |feedback_param|.
2483template <class T, class U>
2484void UpdateCodec(MediaContentDescription* content_desc, int payload_type,
2485 const cricket::FeedbackParam& feedback_param) {
2486 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002487 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2488 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002489 AddFeedbackParameter(feedback_param, &new_codec);
2490 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2491}
2492
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002493template <class T>
2494bool PopWildcardCodec(std::vector<T>* codecs, T* wildcard_codec) {
2495 for (auto iter = codecs->begin(); iter != codecs->end(); ++iter) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002496 if (iter->id == kWildcardPayloadType) {
2497 *wildcard_codec = *iter;
2498 codecs->erase(iter);
2499 return true;
2500 }
2501 }
2502 return false;
2503}
2504
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002505template<class T>
2506void UpdateFromWildcardCodecs(cricket::MediaContentDescriptionImpl<T>* desc) {
2507 auto codecs = desc->codecs();
2508 T wildcard_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002509 if (!PopWildcardCodec(&codecs, &wildcard_codec)) {
2510 return;
2511 }
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002512 for (auto& codec : codecs) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002513 AddFeedbackParameters(wildcard_codec.feedback_params, &codec);
2514 }
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002515 desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002516}
2517
2518void AddAudioAttribute(const std::string& name, const std::string& value,
2519 AudioContentDescription* audio_desc) {
2520 if (value.empty()) {
2521 return;
2522 }
2523 std::vector<cricket::AudioCodec> codecs = audio_desc->codecs();
2524 for (std::vector<cricket::AudioCodec>::iterator iter = codecs.begin();
2525 iter != codecs.end(); ++iter) {
2526 iter->params[name] = value;
2527 }
2528 audio_desc->set_codecs(codecs);
2529}
2530
2531bool ParseContent(const std::string& message,
2532 const MediaType media_type,
2533 int mline_index,
2534 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -07002535 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002536 size_t* pos,
2537 std::string* content_name,
2538 MediaContentDescription* media_desc,
2539 TransportDescription* transport,
2540 std::vector<JsepIceCandidate*>* candidates,
2541 SdpParseError* error) {
2542 ASSERT(media_desc != NULL);
2543 ASSERT(content_name != NULL);
2544 ASSERT(transport != NULL);
2545
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +00002546 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
2547 MaybeCreateStaticPayloadAudioCodecs(
deadbeef67cf2c12016-04-13 10:07:16 -07002548 payload_types, static_cast<AudioContentDescription*>(media_desc));
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +00002549 }
2550
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002551 // The media level "ice-ufrag" and "ice-pwd".
2552 // The candidates before update the media level "ice-pwd" and "ice-ufrag".
2553 Candidates candidates_orig;
2554 std::string line;
2555 std::string mline_id;
2556 // Tracks created out of the ssrc attributes.
2557 StreamParamsVec tracks;
2558 SsrcInfoVec ssrc_infos;
2559 SsrcGroupVec ssrc_groups;
2560 std::string maxptime_as_string;
2561 std::string ptime_as_string;
deadbeef9d3584c2016-02-16 17:54:10 -08002562 std::string stream_id;
2563 std::string track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002564
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002565 // Loop until the next m line
2566 while (!IsLineType(message, kLineTypeMedia, *pos)) {
2567 if (!GetLine(message, pos, &line)) {
2568 if (*pos >= message.size()) {
2569 break; // Done parsing
2570 } else {
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +00002571 return ParseFailed(message, *pos, "Invalid SDP line.", error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002572 }
2573 }
2574
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002575 // RFC 4566
2576 // b=* (zero or more bandwidth information lines)
2577 if (IsLineType(line, kLineTypeSessionBandwidth)) {
2578 std::string bandwidth;
2579 if (HasAttribute(line, kApplicationSpecificMaximum)) {
2580 if (!GetValue(line, kApplicationSpecificMaximum, &bandwidth, error)) {
2581 return false;
2582 } else {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002583 int b = 0;
2584 if (!GetValueFromString(line, bandwidth, &b, error)) {
2585 return false;
2586 }
Peter Thatcherc0c3a862015-06-24 15:31:25 -07002587 // We should never use more than the default bandwidth for RTP-based
2588 // data channels. Don't allow SDP to set the bandwidth, because
2589 // that would give JS the opportunity to "break the Internet".
2590 // See: https://code.google.com/p/chromium/issues/detail?id=280726
2591 if (media_type == cricket::MEDIA_TYPE_DATA && IsRtp(protocol) &&
2592 b > cricket::kDataMaxBandwidth / 1000) {
2593 std::ostringstream description;
2594 description << "RTP-based data channels may not send more than "
2595 << cricket::kDataMaxBandwidth / 1000 << "kbps.";
2596 return ParseFailed(line, description.str(), error);
2597 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002598 media_desc->set_bandwidth(b * 1000);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002599 }
2600 }
2601 continue;
2602 }
2603
2604 if (!IsLineType(line, kLineTypeAttributes)) {
2605 // TODO: Handle other lines if needed.
2606 LOG(LS_INFO) << "Ignored line: " << line;
2607 continue;
2608 }
2609
2610 // Handle attributes common to SCTP and RTP.
2611 if (HasAttribute(line, kAttributeMid)) {
2612 // RFC 3388
2613 // mid-attribute = "a=mid:" identification-tag
2614 // identification-tag = token
2615 // Use the mid identification-tag as the content name.
2616 if (!GetValue(line, kAttributeMid, &mline_id, error)) {
2617 return false;
2618 }
2619 *content_name = mline_id;
2620 } else if (HasAttribute(line, kAttributeCandidate)) {
2621 Candidate candidate;
2622 if (!ParseCandidate(line, &candidate, error, false)) {
2623 return false;
2624 }
2625 candidates_orig.push_back(candidate);
2626 } else if (HasAttribute(line, kAttributeIceUfrag)) {
2627 if (!GetValue(line, kAttributeIceUfrag, &transport->ice_ufrag, error)) {
2628 return false;
2629 }
2630 } else if (HasAttribute(line, kAttributeIcePwd)) {
2631 if (!GetValue(line, kAttributeIcePwd, &transport->ice_pwd, error)) {
2632 return false;
2633 }
2634 } else if (HasAttribute(line, kAttributeIceOption)) {
2635 if (!ParseIceOptions(line, &transport->transport_options, error)) {
2636 return false;
2637 }
2638 } else if (HasAttribute(line, kAttributeFmtp)) {
2639 if (!ParseFmtpAttributes(line, media_type, media_desc, error)) {
2640 return false;
2641 }
2642 } else if (HasAttribute(line, kAttributeFingerprint)) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002643 rtc::SSLFingerprint* fingerprint = NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002644
2645 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
2646 return false;
2647 }
2648 transport->identity_fingerprint.reset(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002649 } else if (HasAttribute(line, kAttributeSetup)) {
2650 if (!ParseDtlsSetup(line, &(transport->connection_role), error)) {
2651 return false;
2652 }
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002653 } else if (IsDtlsSctp(protocol) && HasAttribute(line, kAttributeSctpPort)) {
deadbeef7e146cb2016-09-28 10:04:34 -07002654 if (media_type != cricket::MEDIA_TYPE_DATA) {
2655 return ParseFailed(
2656 line, "sctp-port attribute found in non-data media description.",
2657 error);
2658 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002659 int sctp_port;
2660 if (!ParseSctpPort(line, &sctp_port, error)) {
2661 return false;
2662 }
2663 if (!AddSctpDataCodec(static_cast<DataContentDescription*>(media_desc),
2664 sctp_port)) {
2665 return false;
2666 }
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002667 } else if (IsRtp(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002668 //
2669 // RTP specific attrubtes
2670 //
2671 if (HasAttribute(line, kAttributeRtcpMux)) {
2672 media_desc->set_rtcp_mux(true);
deadbeef13871492015-12-09 12:37:51 -08002673 } else if (HasAttribute(line, kAttributeRtcpReducedSize)) {
2674 media_desc->set_rtcp_reduced_size(true);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002675 } else if (HasAttribute(line, kAttributeSsrcGroup)) {
2676 if (!ParseSsrcGroupAttribute(line, &ssrc_groups, error)) {
2677 return false;
2678 }
2679 } else if (HasAttribute(line, kAttributeSsrc)) {
2680 if (!ParseSsrcAttribute(line, &ssrc_infos, error)) {
2681 return false;
2682 }
2683 } else if (HasAttribute(line, kAttributeCrypto)) {
2684 if (!ParseCryptoAttribute(line, media_desc, error)) {
2685 return false;
2686 }
2687 } else if (HasAttribute(line, kAttributeRtpmap)) {
deadbeef67cf2c12016-04-13 10:07:16 -07002688 if (!ParseRtpmapAttribute(line, media_type, payload_types, media_desc,
2689 error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002690 return false;
2691 }
2692 } else if (HasAttribute(line, kCodecParamMaxPTime)) {
2693 if (!GetValue(line, kCodecParamMaxPTime, &maxptime_as_string, error)) {
2694 return false;
2695 }
2696 } else if (HasAttribute(line, kAttributeRtcpFb)) {
2697 if (!ParseRtcpFbAttribute(line, media_type, media_desc, error)) {
2698 return false;
2699 }
2700 } else if (HasAttribute(line, kCodecParamPTime)) {
2701 if (!GetValue(line, kCodecParamPTime, &ptime_as_string, error)) {
2702 return false;
2703 }
2704 } else if (HasAttribute(line, kAttributeSendOnly)) {
2705 media_desc->set_direction(cricket::MD_SENDONLY);
2706 } else if (HasAttribute(line, kAttributeRecvOnly)) {
2707 media_desc->set_direction(cricket::MD_RECVONLY);
2708 } else if (HasAttribute(line, kAttributeInactive)) {
2709 media_desc->set_direction(cricket::MD_INACTIVE);
2710 } else if (HasAttribute(line, kAttributeSendRecv)) {
2711 media_desc->set_direction(cricket::MD_SENDRECV);
2712 } else if (HasAttribute(line, kAttributeExtmap)) {
isheriff6f8d6862016-05-26 11:24:55 -07002713 RtpExtension extmap;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002714 if (!ParseExtmap(line, &extmap, error)) {
2715 return false;
2716 }
2717 media_desc->AddRtpHeaderExtension(extmap);
2718 } else if (HasAttribute(line, kAttributeXGoogleFlag)) {
2719 // Experimental attribute. Conference mode activates more aggressive
2720 // AEC and NS settings.
2721 // TODO: expose API to set these directly.
2722 std::string flag_value;
2723 if (!GetValue(line, kAttributeXGoogleFlag, &flag_value, error)) {
2724 return false;
2725 }
2726 if (flag_value.compare(kValueConference) == 0)
2727 media_desc->set_conference_mode(true);
deadbeef9d3584c2016-02-16 17:54:10 -08002728 } else if (HasAttribute(line, kAttributeMsid)) {
2729 if (!ParseMsidAttribute(line, &stream_id, &track_id, error)) {
2730 return false;
2731 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002732 }
2733 } else {
2734 // Only parse lines that we are interested of.
2735 LOG(LS_INFO) << "Ignored line: " << line;
2736 continue;
2737 }
2738 }
2739
2740 // Create tracks from the |ssrc_infos|.
Taylor Brandstetter5de6b752016-03-09 17:02:30 -08002741 // If the stream_id/track_id for all SSRCS are identical, one StreamParams
2742 // will be created in CreateTracksFromSsrcInfos, containing all the SSRCs from
2743 // the m= section.
2744 CreateTracksFromSsrcInfos(ssrc_infos, stream_id, track_id, &tracks);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002745
2746 // Add the ssrc group to the track.
2747 for (SsrcGroupVec::iterator ssrc_group = ssrc_groups.begin();
2748 ssrc_group != ssrc_groups.end(); ++ssrc_group) {
2749 if (ssrc_group->ssrcs.empty()) {
2750 continue;
2751 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002752 uint32_t ssrc = ssrc_group->ssrcs.front();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002753 for (StreamParamsVec::iterator track = tracks.begin();
2754 track != tracks.end(); ++track) {
2755 if (track->has_ssrc(ssrc)) {
2756 track->ssrc_groups.push_back(*ssrc_group);
2757 }
2758 }
2759 }
2760
2761 // Add the new tracks to the |media_desc|.
deadbeef9d3584c2016-02-16 17:54:10 -08002762 for (StreamParams& track : tracks) {
2763 media_desc->AddStream(track);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002764 }
2765
2766 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
2767 AudioContentDescription* audio_desc =
2768 static_cast<AudioContentDescription*>(media_desc);
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002769 UpdateFromWildcardCodecs(audio_desc);
2770
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002771 // Verify audio codec ensures that no audio codec has been populated with
2772 // only fmtp.
2773 if (!VerifyAudioCodecs(audio_desc)) {
2774 return ParseFailed("Failed to parse audio codecs correctly.", error);
2775 }
2776 AddAudioAttribute(kCodecParamMaxPTime, maxptime_as_string, audio_desc);
2777 AddAudioAttribute(kCodecParamPTime, ptime_as_string, audio_desc);
2778 }
2779
2780 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002781 VideoContentDescription* video_desc =
2782 static_cast<VideoContentDescription*>(media_desc);
2783 UpdateFromWildcardCodecs(video_desc);
2784 // Verify video codec ensures that no video codec has been populated with
2785 // only rtcp-fb.
2786 if (!VerifyVideoCodecs(video_desc)) {
2787 return ParseFailed("Failed to parse video codecs correctly.", error);
2788 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002789 }
2790
2791 // RFC 5245
2792 // Update the candidates with the media level "ice-pwd" and "ice-ufrag".
2793 for (Candidates::iterator it = candidates_orig.begin();
2794 it != candidates_orig.end(); ++it) {
honghaiza54a0802015-12-16 18:37:23 -08002795 ASSERT((*it).username().empty() ||
2796 (*it).username() == transport->ice_ufrag);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002797 (*it).set_username(transport->ice_ufrag);
2798 ASSERT((*it).password().empty());
2799 (*it).set_password(transport->ice_pwd);
2800 candidates->push_back(
2801 new JsepIceCandidate(mline_id, mline_index, *it));
2802 }
2803 return true;
2804}
2805
2806bool ParseSsrcAttribute(const std::string& line, SsrcInfoVec* ssrc_infos,
2807 SdpParseError* error) {
2808 ASSERT(ssrc_infos != NULL);
2809 // RFC 5576
2810 // a=ssrc:<ssrc-id> <attribute>
2811 // a=ssrc:<ssrc-id> <attribute>:<value>
2812 std::string field1, field2;
Donald Curtis144d0182015-05-15 13:14:24 -07002813 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
2814 &field1, &field2)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002815 const size_t expected_fields = 2;
2816 return ParseFailedExpectFieldNum(line, expected_fields, error);
2817 }
2818
2819 // ssrc:<ssrc-id>
2820 std::string ssrc_id_s;
2821 if (!GetValue(field1, kAttributeSsrc, &ssrc_id_s, error)) {
2822 return false;
2823 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002824 uint32_t ssrc_id = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002825 if (!GetValueFromString(line, ssrc_id_s, &ssrc_id, error)) {
2826 return false;
2827 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002828
2829 std::string attribute;
2830 std::string value;
Donald Curtis144d0182015-05-15 13:14:24 -07002831 if (!rtc::tokenize_first(field2, kSdpDelimiterColon, &attribute, &value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002832 std::ostringstream description;
2833 description << "Failed to get the ssrc attribute value from " << field2
2834 << ". Expected format <attribute>:<value>.";
2835 return ParseFailed(line, description.str(), error);
2836 }
2837
2838 // Check if there's already an item for this |ssrc_id|. Create a new one if
2839 // there isn't.
2840 SsrcInfoVec::iterator ssrc_info = ssrc_infos->begin();
2841 for (; ssrc_info != ssrc_infos->end(); ++ssrc_info) {
2842 if (ssrc_info->ssrc_id == ssrc_id) {
2843 break;
2844 }
2845 }
2846 if (ssrc_info == ssrc_infos->end()) {
2847 SsrcInfo info;
2848 info.ssrc_id = ssrc_id;
2849 ssrc_infos->push_back(info);
2850 ssrc_info = ssrc_infos->end() - 1;
2851 }
2852
2853 // Store the info to the |ssrc_info|.
2854 if (attribute == kSsrcAttributeCname) {
2855 // RFC 5576
2856 // cname:<value>
2857 ssrc_info->cname = value;
2858 } else if (attribute == kSsrcAttributeMsid) {
2859 // draft-alvestrand-mmusic-msid-00
2860 // "msid:" identifier [ " " appdata ]
2861 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002862 rtc::split(value, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002863 if (fields.size() < 1 || fields.size() > 2) {
2864 return ParseFailed(line,
2865 "Expected format \"msid:<identifier>[ <appdata>]\".",
2866 error);
2867 }
deadbeef9d3584c2016-02-16 17:54:10 -08002868 ssrc_info->stream_id = fields[0];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002869 if (fields.size() == 2) {
deadbeef9d3584c2016-02-16 17:54:10 -08002870 ssrc_info->track_id = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002871 }
2872 } else if (attribute == kSsrcAttributeMslabel) {
2873 // draft-alvestrand-rtcweb-mid-01
2874 // mslabel:<value>
2875 ssrc_info->mslabel = value;
2876 } else if (attribute == kSSrcAttributeLabel) {
2877 // The label isn't defined.
2878 // label:<value>
2879 ssrc_info->label = value;
2880 }
2881 return true;
2882}
2883
2884bool ParseSsrcGroupAttribute(const std::string& line,
2885 SsrcGroupVec* ssrc_groups,
2886 SdpParseError* error) {
2887 ASSERT(ssrc_groups != NULL);
2888 // RFC 5576
2889 // a=ssrc-group:<semantics> <ssrc-id> ...
2890 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002891 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002892 kSdpDelimiterSpace, &fields);
2893 const size_t expected_min_fields = 2;
2894 if (fields.size() < expected_min_fields) {
2895 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2896 }
2897 std::string semantics;
2898 if (!GetValue(fields[0], kAttributeSsrcGroup, &semantics, error)) {
2899 return false;
2900 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002901 std::vector<uint32_t> ssrcs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002902 for (size_t i = 1; i < fields.size(); ++i) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02002903 uint32_t ssrc = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002904 if (!GetValueFromString(line, fields[i], &ssrc, error)) {
2905 return false;
2906 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002907 ssrcs.push_back(ssrc);
2908 }
2909 ssrc_groups->push_back(SsrcGroup(semantics, ssrcs));
2910 return true;
2911}
2912
2913bool ParseCryptoAttribute(const std::string& line,
2914 MediaContentDescription* media_desc,
2915 SdpParseError* error) {
2916 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002917 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002918 kSdpDelimiterSpace, &fields);
2919 // RFC 4568
2920 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
2921 const size_t expected_min_fields = 3;
2922 if (fields.size() < expected_min_fields) {
2923 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2924 }
2925 std::string tag_value;
2926 if (!GetValue(fields[0], kAttributeCrypto, &tag_value, error)) {
2927 return false;
2928 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002929 int tag = 0;
2930 if (!GetValueFromString(line, tag_value, &tag, error)) {
2931 return false;
2932 }
jbauch083b73f2015-07-16 02:46:32 -07002933 const std::string& crypto_suite = fields[1];
2934 const std::string& key_params = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002935 std::string session_params;
2936 if (fields.size() > 3) {
2937 session_params = fields[3];
2938 }
2939 media_desc->AddCrypto(CryptoParams(tag, crypto_suite, key_params,
2940 session_params));
2941 return true;
2942}
2943
2944// Updates or creates a new codec entry in the audio description with according
deadbeef67cf2c12016-04-13 10:07:16 -07002945// to |name|, |clockrate|, |bitrate|, and |channels|.
2946void UpdateCodec(int payload_type,
2947 const std::string& name,
2948 int clockrate,
2949 int bitrate,
2950 size_t channels,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002951 AudioContentDescription* audio_desc) {
2952 // Codec may already be populated with (only) optional parameters
2953 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002954 cricket::AudioCodec codec =
2955 GetCodecWithPayloadType(audio_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002956 codec.name = name;
2957 codec.clockrate = clockrate;
2958 codec.bitrate = bitrate;
2959 codec.channels = channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002960 AddOrReplaceCodec<AudioContentDescription, cricket::AudioCodec>(audio_desc,
2961 codec);
2962}
2963
2964// Updates or creates a new codec entry in the video description according to
deadbeef67cf2c12016-04-13 10:07:16 -07002965// |name|, |width|, |height|, and |framerate|.
2966void UpdateCodec(int payload_type,
2967 const std::string& name,
2968 int width,
2969 int height,
2970 int framerate,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002971 VideoContentDescription* video_desc) {
2972 // Codec may already be populated with (only) optional parameters
2973 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002974 cricket::VideoCodec codec =
2975 GetCodecWithPayloadType(video_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002976 codec.name = name;
2977 codec.width = width;
2978 codec.height = height;
2979 codec.framerate = framerate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002980 AddOrReplaceCodec<VideoContentDescription, cricket::VideoCodec>(video_desc,
2981 codec);
2982}
2983
2984bool ParseRtpmapAttribute(const std::string& line,
2985 const MediaType media_type,
deadbeef67cf2c12016-04-13 10:07:16 -07002986 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002987 MediaContentDescription* media_desc,
2988 SdpParseError* error) {
2989 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002990 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002991 kSdpDelimiterSpace, &fields);
2992 // RFC 4566
2993 // a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encodingparameters>]
2994 const size_t expected_min_fields = 2;
2995 if (fields.size() < expected_min_fields) {
2996 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2997 }
2998 std::string payload_type_value;
2999 if (!GetValue(fields[0], kAttributeRtpmap, &payload_type_value, error)) {
3000 return false;
3001 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003002 int payload_type = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00003003 if (!GetPayloadTypeFromString(line, payload_type_value, &payload_type,
3004 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003005 return false;
3006 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003007
deadbeef67cf2c12016-04-13 10:07:16 -07003008 if (std::find(payload_types.begin(), payload_types.end(), payload_type) ==
3009 payload_types.end()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003010 LOG(LS_WARNING) << "Ignore rtpmap line that did not appear in the "
3011 << "<fmt> of the m-line: " << line;
3012 return true;
3013 }
jbauch083b73f2015-07-16 02:46:32 -07003014 const std::string& encoder = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003015 std::vector<std::string> codec_params;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003016 rtc::split(encoder, '/', &codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003017 // <encoding name>/<clock rate>[/<encodingparameters>]
3018 // 2 mandatory fields
3019 if (codec_params.size() < 2 || codec_params.size() > 3) {
3020 return ParseFailed(line,
3021 "Expected format \"<encoding name>/<clock rate>"
3022 "[/<encodingparameters>]\".",
3023 error);
3024 }
jbauch083b73f2015-07-16 02:46:32 -07003025 const std::string& encoding_name = codec_params[0];
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003026 int clock_rate = 0;
3027 if (!GetValueFromString(line, codec_params[1], &clock_rate, error)) {
3028 return false;
3029 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003030 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3031 VideoContentDescription* video_desc =
3032 static_cast<VideoContentDescription*>(media_desc);
3033 // TODO: We will send resolution in SDP. For now use
3034 // JsepSessionDescription::kMaxVideoCodecWidth and kMaxVideoCodecHeight.
3035 UpdateCodec(payload_type, encoding_name,
3036 JsepSessionDescription::kMaxVideoCodecWidth,
3037 JsepSessionDescription::kMaxVideoCodecHeight,
3038 JsepSessionDescription::kDefaultVideoCodecFramerate,
deadbeef67cf2c12016-04-13 10:07:16 -07003039 video_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003040 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3041 // RFC 4566
3042 // For audio streams, <encoding parameters> indicates the number
3043 // of audio channels. This parameter is OPTIONAL and may be
3044 // omitted if the number of channels is one, provided that no
3045 // additional parameters are needed.
Peter Kasting69558702016-01-12 16:26:35 -08003046 size_t channels = 1;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003047 if (codec_params.size() == 3) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003048 if (!GetValueFromString(line, codec_params[2], &channels, error)) {
3049 return false;
3050 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003051 }
3052 int bitrate = 0;
3053 // The default behavior for ISAC (bitrate == 0) in webrtcvoiceengine.cc
3054 // (specifically FindWebRtcCodec) is bandwidth-adaptive variable bitrate.
3055 // The bandwidth adaptation doesn't always work well, so this code
3056 // sets a fixed target bitrate instead.
3057 if (_stricmp(encoding_name.c_str(), kIsacCodecName) == 0) {
3058 if (clock_rate <= 16000) {
3059 bitrate = kIsacWbDefaultRate;
3060 } else {
3061 bitrate = kIsacSwbDefaultRate;
3062 }
3063 }
3064 AudioContentDescription* audio_desc =
3065 static_cast<AudioContentDescription*>(media_desc);
3066 UpdateCodec(payload_type, encoding_name, clock_rate, bitrate, channels,
deadbeef67cf2c12016-04-13 10:07:16 -07003067 audio_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003068 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
3069 DataContentDescription* data_desc =
3070 static_cast<DataContentDescription*>(media_desc);
deadbeef67cf2c12016-04-13 10:07:16 -07003071 data_desc->AddCodec(cricket::DataCodec(payload_type, encoding_name));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003072 }
3073 return true;
3074}
3075
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003076bool ParseFmtpParam(const std::string& line, std::string* parameter,
3077 std::string* value, SdpParseError* error) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003078 if (!rtc::tokenize_first(line, kSdpDelimiterEqual, parameter, value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003079 ParseFailed(line, "Unable to parse fmtp parameter. \'=\' missing.", error);
3080 return false;
3081 }
3082 // a=fmtp:<payload_type> <param1>=<value1>; <param2>=<value2>; ...
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003083 return true;
3084}
3085
3086bool ParseFmtpAttributes(const std::string& line, const MediaType media_type,
3087 MediaContentDescription* media_desc,
3088 SdpParseError* error) {
3089 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3090 media_type != cricket::MEDIA_TYPE_VIDEO) {
3091 return true;
3092 }
Donald Curtis0e07f922015-05-15 09:21:23 -07003093
3094 std::string line_payload;
3095 std::string line_params;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003096
3097 // RFC 5576
3098 // a=fmtp:<format> <format specific parameters>
3099 // At least two fields, whereas the second one is any of the optional
3100 // parameters.
Donald Curtis144d0182015-05-15 13:14:24 -07003101 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
3102 &line_payload, &line_params)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003103 ParseFailedExpectMinFieldNum(line, 2, error);
3104 return false;
3105 }
3106
Donald Curtis0e07f922015-05-15 09:21:23 -07003107 // Parse out the payload information.
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003108 std::string payload_type_str;
Donald Curtis0e07f922015-05-15 09:21:23 -07003109 if (!GetValue(line_payload, kAttributeFmtp, &payload_type_str, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003110 return false;
3111 }
3112
Donald Curtis0e07f922015-05-15 09:21:23 -07003113 int payload_type = 0;
Donald Curtis144d0182015-05-15 13:14:24 -07003114 if (!GetPayloadTypeFromString(line_payload, payload_type_str, &payload_type,
3115 error)) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003116 return false;
3117 }
3118
3119 // Parse out format specific parameters.
3120 std::vector<std::string> fields;
3121 rtc::split(line_params, kSdpDelimiterSemicolon, &fields);
3122
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003123 cricket::CodecParameterMap codec_params;
Donald Curtis144d0182015-05-15 13:14:24 -07003124 for (auto& iter : fields) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003125 if (iter.find(kSdpDelimiterEqual) == std::string::npos) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003126 // Only fmtps with equals are currently supported. Other fmtp types
3127 // should be ignored. Unknown fmtps do not constitute an error.
3128 continue;
3129 }
Donald Curtis0e07f922015-05-15 09:21:23 -07003130
3131 std::string name;
3132 std::string value;
3133 if (!ParseFmtpParam(rtc::string_trim(iter), &name, &value, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003134 return false;
3135 }
3136 codec_params[name] = value;
3137 }
3138
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003139 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3140 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003141 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003142 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3143 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003144 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003145 }
3146 return true;
3147}
3148
3149bool ParseRtcpFbAttribute(const std::string& line, const MediaType media_type,
3150 MediaContentDescription* media_desc,
3151 SdpParseError* error) {
3152 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3153 media_type != cricket::MEDIA_TYPE_VIDEO) {
3154 return true;
3155 }
3156 std::vector<std::string> rtcp_fb_fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003157 rtc::split(line.c_str(), kSdpDelimiterSpace, &rtcp_fb_fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003158 if (rtcp_fb_fields.size() < 2) {
3159 return ParseFailedGetValue(line, kAttributeRtcpFb, error);
3160 }
3161 std::string payload_type_string;
3162 if (!GetValue(rtcp_fb_fields[0], kAttributeRtcpFb, &payload_type_string,
3163 error)) {
3164 return false;
3165 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003166 int payload_type = kWildcardPayloadType;
3167 if (payload_type_string != "*") {
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00003168 if (!GetPayloadTypeFromString(line, payload_type_string, &payload_type,
3169 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003170 return false;
3171 }
3172 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003173 std::string id = rtcp_fb_fields[1];
3174 std::string param = "";
3175 for (std::vector<std::string>::iterator iter = rtcp_fb_fields.begin() + 2;
3176 iter != rtcp_fb_fields.end(); ++iter) {
3177 param.append(*iter);
3178 }
3179 const cricket::FeedbackParam feedback_param(id, param);
3180
3181 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003182 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
3183 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003184 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003185 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
3186 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003187 }
3188 return true;
3189}
3190
3191} // namespace webrtc