1/*
2 *  Copyright 2015 The WebRTC project authors. All Rights Reserved.
3 *
4 *  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.
9 */
10
11#import "RTCConfiguration+Private.h"
12
13#include <memory>
14
15#import "RTCCertificate.h"
16#import "RTCConfiguration+Native.h"
17#import "RTCIceServer+Private.h"
18#import "base/RTCLogging.h"
19
20#include "rtc_base/rtc_certificate_generator.h"
21#include "rtc_base/ssl_identity.h"
22
23@implementation RTC_OBJC_TYPE (RTCConfiguration)
24
25@synthesize enableDscp = _enableDscp;
26@synthesize iceServers = _iceServers;
27@synthesize certificate = _certificate;
28@synthesize iceTransportPolicy = _iceTransportPolicy;
29@synthesize bundlePolicy = _bundlePolicy;
30@synthesize rtcpMuxPolicy = _rtcpMuxPolicy;
31@synthesize tcpCandidatePolicy = _tcpCandidatePolicy;
32@synthesize candidateNetworkPolicy = _candidateNetworkPolicy;
33@synthesize continualGatheringPolicy = _continualGatheringPolicy;
34@synthesize disableIPV6 = _disableIPV6;
35@synthesize disableIPV6OnWiFi = _disableIPV6OnWiFi;
36@synthesize maxIPv6Networks = _maxIPv6Networks;
37@synthesize disableLinkLocalNetworks = _disableLinkLocalNetworks;
38@synthesize audioJitterBufferMaxPackets = _audioJitterBufferMaxPackets;
39@synthesize audioJitterBufferFastAccelerate = _audioJitterBufferFastAccelerate;
40@synthesize iceConnectionReceivingTimeout = _iceConnectionReceivingTimeout;
41@synthesize iceBackupCandidatePairPingInterval =
42    _iceBackupCandidatePairPingInterval;
43@synthesize keyType = _keyType;
44@synthesize iceCandidatePoolSize = _iceCandidatePoolSize;
45@synthesize shouldPruneTurnPorts = _shouldPruneTurnPorts;
46@synthesize shouldPresumeWritableWhenFullyRelayed =
47    _shouldPresumeWritableWhenFullyRelayed;
48@synthesize shouldSurfaceIceCandidatesOnIceTransportTypeChanged =
49    _shouldSurfaceIceCandidatesOnIceTransportTypeChanged;
50@synthesize iceCheckMinInterval = _iceCheckMinInterval;
51@synthesize sdpSemantics = _sdpSemantics;
52@synthesize turnCustomizer = _turnCustomizer;
53@synthesize activeResetSrtpParams = _activeResetSrtpParams;
54@synthesize allowCodecSwitching = _allowCodecSwitching;
55@synthesize cryptoOptions = _cryptoOptions;
56@synthesize rtcpAudioReportIntervalMs = _rtcpAudioReportIntervalMs;
57@synthesize rtcpVideoReportIntervalMs = _rtcpVideoReportIntervalMs;
58
59- (instancetype)init {
60  // Copy defaults.
61  webrtc::PeerConnectionInterface::RTCConfiguration config;
62  return [self initWithNativeConfiguration:config];
63}
64
65- (instancetype)initWithNativeConfiguration:
66    (const webrtc::PeerConnectionInterface::RTCConfiguration &)config {
67  if (self = [super init]) {
68    _enableDscp = config.dscp();
69    NSMutableArray *iceServers = [NSMutableArray array];
70    for (const webrtc::PeerConnectionInterface::IceServer& server : config.servers) {
71      RTC_OBJC_TYPE(RTCIceServer) *iceServer =
72          [[RTC_OBJC_TYPE(RTCIceServer) alloc] initWithNativeServer:server];
73      [iceServers addObject:iceServer];
74    }
75    _iceServers = iceServers;
76    if (!config.certificates.empty()) {
77      rtc::scoped_refptr<rtc::RTCCertificate> native_cert;
78      native_cert = config.certificates[0];
79      rtc::RTCCertificatePEM native_pem = native_cert->ToPEM();
80      _certificate = [[RTC_OBJC_TYPE(RTCCertificate) alloc]
81          initWithPrivateKey:@(native_pem.private_key().c_str())
82                 certificate:@(native_pem.certificate().c_str())];
83    }
84    _iceTransportPolicy =
85        [[self class] transportPolicyForTransportsType:config.type];
86    _bundlePolicy =
87        [[self class] bundlePolicyForNativePolicy:config.bundle_policy];
88    _rtcpMuxPolicy =
89        [[self class] rtcpMuxPolicyForNativePolicy:config.rtcp_mux_policy];
90    _tcpCandidatePolicy = [[self class] tcpCandidatePolicyForNativePolicy:
91        config.tcp_candidate_policy];
92    _candidateNetworkPolicy = [[self class]
93        candidateNetworkPolicyForNativePolicy:config.candidate_network_policy];
94    webrtc::PeerConnectionInterface::ContinualGatheringPolicy nativePolicy =
95    config.continual_gathering_policy;
96    _continualGatheringPolicy =
97        [[self class] continualGatheringPolicyForNativePolicy:nativePolicy];
98    _disableIPV6 = config.disable_ipv6;
99    _disableIPV6OnWiFi = config.disable_ipv6_on_wifi;
100    _maxIPv6Networks = config.max_ipv6_networks;
101    _disableLinkLocalNetworks = config.disable_link_local_networks;
102    _audioJitterBufferMaxPackets = config.audio_jitter_buffer_max_packets;
103    _audioJitterBufferFastAccelerate = config.audio_jitter_buffer_fast_accelerate;
104    _iceConnectionReceivingTimeout = config.ice_connection_receiving_timeout;
105    _iceBackupCandidatePairPingInterval =
106        config.ice_backup_candidate_pair_ping_interval;
107    _keyType = RTCEncryptionKeyTypeECDSA;
108    _iceCandidatePoolSize = config.ice_candidate_pool_size;
109    _shouldPruneTurnPorts = config.prune_turn_ports;
110    _shouldPresumeWritableWhenFullyRelayed =
111        config.presume_writable_when_fully_relayed;
112    _shouldSurfaceIceCandidatesOnIceTransportTypeChanged =
113        config.surface_ice_candidates_on_ice_transport_type_changed;
114    if (config.ice_check_min_interval) {
115      _iceCheckMinInterval =
116          [NSNumber numberWithInt:*config.ice_check_min_interval];
117    }
118    _sdpSemantics = [[self class] sdpSemanticsForNativeSdpSemantics:config.sdp_semantics];
119    _turnCustomizer = config.turn_customizer;
120    _activeResetSrtpParams = config.active_reset_srtp_params;
121    if (config.crypto_options) {
122      _cryptoOptions = [[RTC_OBJC_TYPE(RTCCryptoOptions) alloc]
123               initWithSrtpEnableGcmCryptoSuites:config.crypto_options->srtp
124                                                     .enable_gcm_crypto_suites
125             srtpEnableAes128Sha1_32CryptoCipher:config.crypto_options->srtp
126                                                     .enable_aes128_sha1_32_crypto_cipher
127          srtpEnableEncryptedRtpHeaderExtensions:config.crypto_options->srtp
128                                                     .enable_encrypted_rtp_header_extensions
129                    sframeRequireFrameEncryption:config.crypto_options->sframe
130                                                     .require_frame_encryption];
131    }
132    _rtcpAudioReportIntervalMs = config.audio_rtcp_report_interval_ms();
133    _rtcpVideoReportIntervalMs = config.video_rtcp_report_interval_ms();
134    _allowCodecSwitching = config.allow_codec_switching.value_or(false);
135  }
136  return self;
137}
138
139- (NSString *)description {
140  static NSString *formatString = @"RTC_OBJC_TYPE(RTCConfiguration): "
141                                  @"{\n%@\n%@\n%@\n%@\n%@\n%@\n%@\n%@\n%d\n%d\n%d\n%d\n%d\n%d\n"
142                                  @"%d\n%@\n%d\n%d\n%d\n%d\n%d\n%@\n}\n";
143
144  return [NSString
145      stringWithFormat:formatString,
146                       _iceServers,
147                       [[self class] stringForTransportPolicy:_iceTransportPolicy],
148                       [[self class] stringForBundlePolicy:_bundlePolicy],
149                       [[self class] stringForRtcpMuxPolicy:_rtcpMuxPolicy],
150                       [[self class] stringForTcpCandidatePolicy:_tcpCandidatePolicy],
151                       [[self class] stringForCandidateNetworkPolicy:_candidateNetworkPolicy],
152                       [[self class] stringForContinualGatheringPolicy:_continualGatheringPolicy],
153                       [[self class] stringForSdpSemantics:_sdpSemantics],
154                       _audioJitterBufferMaxPackets,
155                       _audioJitterBufferFastAccelerate,
156                       _iceConnectionReceivingTimeout,
157                       _iceBackupCandidatePairPingInterval,
158                       _iceCandidatePoolSize,
159                       _shouldPruneTurnPorts,
160                       _shouldPresumeWritableWhenFullyRelayed,
161                       _shouldSurfaceIceCandidatesOnIceTransportTypeChanged,
162                       _iceCheckMinInterval,
163                       _disableLinkLocalNetworks,
164                       _disableIPV6,
165                       _disableIPV6OnWiFi,
166                       _maxIPv6Networks,
167                       _activeResetSrtpParams,
168                       _enableDscp];
169}
170
171#pragma mark - Private
172
173- (webrtc::PeerConnectionInterface::RTCConfiguration *)
174    createNativeConfiguration {
175  std::unique_ptr<webrtc::PeerConnectionInterface::RTCConfiguration>
176      nativeConfig(new webrtc::PeerConnectionInterface::RTCConfiguration(
177          webrtc::PeerConnectionInterface::RTCConfigurationType::kAggressive));
178
179  nativeConfig->set_dscp(_enableDscp);
180  for (RTC_OBJC_TYPE(RTCIceServer) * iceServer in _iceServers) {
181    nativeConfig->servers.push_back(iceServer.nativeServer);
182  }
183  nativeConfig->type =
184      [[self class] nativeTransportsTypeForTransportPolicy:_iceTransportPolicy];
185  nativeConfig->bundle_policy =
186      [[self class] nativeBundlePolicyForPolicy:_bundlePolicy];
187  nativeConfig->rtcp_mux_policy =
188      [[self class] nativeRtcpMuxPolicyForPolicy:_rtcpMuxPolicy];
189  nativeConfig->tcp_candidate_policy =
190      [[self class] nativeTcpCandidatePolicyForPolicy:_tcpCandidatePolicy];
191  nativeConfig->candidate_network_policy = [[self class]
192      nativeCandidateNetworkPolicyForPolicy:_candidateNetworkPolicy];
193  nativeConfig->continual_gathering_policy = [[self class]
194      nativeContinualGatheringPolicyForPolicy:_continualGatheringPolicy];
195  nativeConfig->disable_ipv6 = _disableIPV6;
196  nativeConfig->disable_ipv6_on_wifi = _disableIPV6OnWiFi;
197  nativeConfig->max_ipv6_networks = _maxIPv6Networks;
198  nativeConfig->disable_link_local_networks = _disableLinkLocalNetworks;
199  nativeConfig->audio_jitter_buffer_max_packets = _audioJitterBufferMaxPackets;
200  nativeConfig->audio_jitter_buffer_fast_accelerate =
201      _audioJitterBufferFastAccelerate  ? true : false;
202  nativeConfig->ice_connection_receiving_timeout =
203      _iceConnectionReceivingTimeout;
204  nativeConfig->ice_backup_candidate_pair_ping_interval =
205      _iceBackupCandidatePairPingInterval;
206  rtc::KeyType keyType =
207      [[self class] nativeEncryptionKeyTypeForKeyType:_keyType];
208  if (_certificate != nullptr) {
209    // if offered a pemcert use it...
210    RTC_LOG(LS_INFO) << "Have configured cert - using it.";
211    std::string pem_private_key = [[_certificate private_key] UTF8String];
212    std::string pem_certificate = [[_certificate certificate] UTF8String];
213    rtc::RTCCertificatePEM pem = rtc::RTCCertificatePEM(pem_private_key, pem_certificate);
214    rtc::scoped_refptr<rtc::RTCCertificate> certificate = rtc::RTCCertificate::FromPEM(pem);
215    RTC_LOG(LS_INFO) << "Created cert from PEM strings.";
216    if (!certificate) {
217      RTC_LOG(LS_ERROR) << "Failed to generate certificate from PEM.";
218      return nullptr;
219    }
220    nativeConfig->certificates.push_back(certificate);
221  } else {
222    RTC_LOG(LS_INFO) << "Don't have configured cert.";
223    // Generate non-default certificate.
224    if (keyType != rtc::KT_DEFAULT) {
225      rtc::scoped_refptr<rtc::RTCCertificate> certificate =
226          rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(keyType),
227                                                            absl::optional<uint64_t>());
228      if (!certificate) {
229        RTCLogError(@"Failed to generate certificate.");
230        return nullptr;
231      }
232      nativeConfig->certificates.push_back(certificate);
233    }
234  }
235  nativeConfig->ice_candidate_pool_size = _iceCandidatePoolSize;
236  nativeConfig->prune_turn_ports = _shouldPruneTurnPorts ? true : false;
237  nativeConfig->presume_writable_when_fully_relayed =
238      _shouldPresumeWritableWhenFullyRelayed ? true : false;
239  nativeConfig->surface_ice_candidates_on_ice_transport_type_changed =
240      _shouldSurfaceIceCandidatesOnIceTransportTypeChanged ? true : false;
241  if (_iceCheckMinInterval != nil) {
242    nativeConfig->ice_check_min_interval = absl::optional<int>(_iceCheckMinInterval.intValue);
243  }
244  nativeConfig->sdp_semantics = [[self class] nativeSdpSemanticsForSdpSemantics:_sdpSemantics];
245  if (_turnCustomizer) {
246    nativeConfig->turn_customizer = _turnCustomizer;
247  }
248  nativeConfig->active_reset_srtp_params = _activeResetSrtpParams ? true : false;
249  if (_cryptoOptions) {
250    webrtc::CryptoOptions nativeCryptoOptions;
251    nativeCryptoOptions.srtp.enable_gcm_crypto_suites =
252        _cryptoOptions.srtpEnableGcmCryptoSuites ? true : false;
253    nativeCryptoOptions.srtp.enable_aes128_sha1_32_crypto_cipher =
254        _cryptoOptions.srtpEnableAes128Sha1_32CryptoCipher ? true : false;
255    nativeCryptoOptions.srtp.enable_encrypted_rtp_header_extensions =
256        _cryptoOptions.srtpEnableEncryptedRtpHeaderExtensions ? true : false;
257    nativeCryptoOptions.sframe.require_frame_encryption =
258        _cryptoOptions.sframeRequireFrameEncryption ? true : false;
259    nativeConfig->crypto_options = absl::optional<webrtc::CryptoOptions>(nativeCryptoOptions);
260  }
261  nativeConfig->set_audio_rtcp_report_interval_ms(_rtcpAudioReportIntervalMs);
262  nativeConfig->set_video_rtcp_report_interval_ms(_rtcpVideoReportIntervalMs);
263  nativeConfig->allow_codec_switching = _allowCodecSwitching;
264  return nativeConfig.release();
265}
266
267+ (webrtc::PeerConnectionInterface::IceTransportsType)
268    nativeTransportsTypeForTransportPolicy:(RTCIceTransportPolicy)policy {
269  switch (policy) {
270    case RTCIceTransportPolicyNone:
271      return webrtc::PeerConnectionInterface::kNone;
272    case RTCIceTransportPolicyRelay:
273      return webrtc::PeerConnectionInterface::kRelay;
274    case RTCIceTransportPolicyNoHost:
275      return webrtc::PeerConnectionInterface::kNoHost;
276    case RTCIceTransportPolicyAll:
277      return webrtc::PeerConnectionInterface::kAll;
278  }
279}
280
281+ (RTCIceTransportPolicy)transportPolicyForTransportsType:
282    (webrtc::PeerConnectionInterface::IceTransportsType)nativeType {
283  switch (nativeType) {
284    case webrtc::PeerConnectionInterface::kNone:
285      return RTCIceTransportPolicyNone;
286    case webrtc::PeerConnectionInterface::kRelay:
287      return RTCIceTransportPolicyRelay;
288    case webrtc::PeerConnectionInterface::kNoHost:
289      return RTCIceTransportPolicyNoHost;
290    case webrtc::PeerConnectionInterface::kAll:
291      return RTCIceTransportPolicyAll;
292  }
293}
294
295+ (NSString *)stringForTransportPolicy:(RTCIceTransportPolicy)policy {
296  switch (policy) {
297    case RTCIceTransportPolicyNone:
298      return @"NONE";
299    case RTCIceTransportPolicyRelay:
300      return @"RELAY";
301    case RTCIceTransportPolicyNoHost:
302      return @"NO_HOST";
303    case RTCIceTransportPolicyAll:
304      return @"ALL";
305  }
306}
307
308+ (webrtc::PeerConnectionInterface::BundlePolicy)nativeBundlePolicyForPolicy:
309    (RTCBundlePolicy)policy {
310  switch (policy) {
311    case RTCBundlePolicyBalanced:
312      return webrtc::PeerConnectionInterface::kBundlePolicyBalanced;
313    case RTCBundlePolicyMaxCompat:
314      return webrtc::PeerConnectionInterface::kBundlePolicyMaxCompat;
315    case RTCBundlePolicyMaxBundle:
316      return webrtc::PeerConnectionInterface::kBundlePolicyMaxBundle;
317  }
318}
319
320+ (RTCBundlePolicy)bundlePolicyForNativePolicy:
321    (webrtc::PeerConnectionInterface::BundlePolicy)nativePolicy {
322  switch (nativePolicy) {
323    case webrtc::PeerConnectionInterface::kBundlePolicyBalanced:
324      return RTCBundlePolicyBalanced;
325    case webrtc::PeerConnectionInterface::kBundlePolicyMaxCompat:
326      return RTCBundlePolicyMaxCompat;
327    case webrtc::PeerConnectionInterface::kBundlePolicyMaxBundle:
328      return RTCBundlePolicyMaxBundle;
329  }
330}
331
332+ (NSString *)stringForBundlePolicy:(RTCBundlePolicy)policy {
333  switch (policy) {
334    case RTCBundlePolicyBalanced:
335      return @"BALANCED";
336    case RTCBundlePolicyMaxCompat:
337      return @"MAX_COMPAT";
338    case RTCBundlePolicyMaxBundle:
339      return @"MAX_BUNDLE";
340  }
341}
342
343+ (webrtc::PeerConnectionInterface::RtcpMuxPolicy)nativeRtcpMuxPolicyForPolicy:
344    (RTCRtcpMuxPolicy)policy {
345  switch (policy) {
346    case RTCRtcpMuxPolicyNegotiate:
347      return webrtc::PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
348    case RTCRtcpMuxPolicyRequire:
349      return webrtc::PeerConnectionInterface::kRtcpMuxPolicyRequire;
350  }
351}
352
353+ (RTCRtcpMuxPolicy)rtcpMuxPolicyForNativePolicy:
354    (webrtc::PeerConnectionInterface::RtcpMuxPolicy)nativePolicy {
355  switch (nativePolicy) {
356    case webrtc::PeerConnectionInterface::kRtcpMuxPolicyNegotiate:
357      return RTCRtcpMuxPolicyNegotiate;
358    case webrtc::PeerConnectionInterface::kRtcpMuxPolicyRequire:
359      return RTCRtcpMuxPolicyRequire;
360  }
361}
362
363+ (NSString *)stringForRtcpMuxPolicy:(RTCRtcpMuxPolicy)policy {
364  switch (policy) {
365    case RTCRtcpMuxPolicyNegotiate:
366      return @"NEGOTIATE";
367    case RTCRtcpMuxPolicyRequire:
368      return @"REQUIRE";
369  }
370}
371
372+ (webrtc::PeerConnectionInterface::TcpCandidatePolicy)
373    nativeTcpCandidatePolicyForPolicy:(RTCTcpCandidatePolicy)policy {
374  switch (policy) {
375    case RTCTcpCandidatePolicyEnabled:
376      return webrtc::PeerConnectionInterface::kTcpCandidatePolicyEnabled;
377    case RTCTcpCandidatePolicyDisabled:
378      return webrtc::PeerConnectionInterface::kTcpCandidatePolicyDisabled;
379  }
380}
381
382+ (webrtc::PeerConnectionInterface::CandidateNetworkPolicy)
383    nativeCandidateNetworkPolicyForPolicy:(RTCCandidateNetworkPolicy)policy {
384  switch (policy) {
385    case RTCCandidateNetworkPolicyAll:
386      return webrtc::PeerConnectionInterface::kCandidateNetworkPolicyAll;
387    case RTCCandidateNetworkPolicyLowCost:
388      return webrtc::PeerConnectionInterface::kCandidateNetworkPolicyLowCost;
389  }
390}
391
392+ (RTCTcpCandidatePolicy)tcpCandidatePolicyForNativePolicy:
393    (webrtc::PeerConnectionInterface::TcpCandidatePolicy)nativePolicy {
394  switch (nativePolicy) {
395    case webrtc::PeerConnectionInterface::kTcpCandidatePolicyEnabled:
396      return RTCTcpCandidatePolicyEnabled;
397    case webrtc::PeerConnectionInterface::kTcpCandidatePolicyDisabled:
398      return RTCTcpCandidatePolicyDisabled;
399  }
400}
401
402+ (NSString *)stringForTcpCandidatePolicy:(RTCTcpCandidatePolicy)policy {
403  switch (policy) {
404    case RTCTcpCandidatePolicyEnabled:
405      return @"TCP_ENABLED";
406    case RTCTcpCandidatePolicyDisabled:
407      return @"TCP_DISABLED";
408  }
409}
410
411+ (RTCCandidateNetworkPolicy)candidateNetworkPolicyForNativePolicy:
412    (webrtc::PeerConnectionInterface::CandidateNetworkPolicy)nativePolicy {
413  switch (nativePolicy) {
414    case webrtc::PeerConnectionInterface::kCandidateNetworkPolicyAll:
415      return RTCCandidateNetworkPolicyAll;
416    case webrtc::PeerConnectionInterface::kCandidateNetworkPolicyLowCost:
417      return RTCCandidateNetworkPolicyLowCost;
418  }
419}
420
421+ (NSString *)stringForCandidateNetworkPolicy:
422    (RTCCandidateNetworkPolicy)policy {
423  switch (policy) {
424    case RTCCandidateNetworkPolicyAll:
425      return @"CANDIDATE_ALL_NETWORKS";
426    case RTCCandidateNetworkPolicyLowCost:
427      return @"CANDIDATE_LOW_COST_NETWORKS";
428  }
429}
430
431+ (webrtc::PeerConnectionInterface::ContinualGatheringPolicy)
432    nativeContinualGatheringPolicyForPolicy:
433        (RTCContinualGatheringPolicy)policy {
434  switch (policy) {
435    case RTCContinualGatheringPolicyGatherOnce:
436      return webrtc::PeerConnectionInterface::GATHER_ONCE;
437    case RTCContinualGatheringPolicyGatherContinually:
438      return webrtc::PeerConnectionInterface::GATHER_CONTINUALLY;
439  }
440}
441
442+ (RTCContinualGatheringPolicy)continualGatheringPolicyForNativePolicy:
443    (webrtc::PeerConnectionInterface::ContinualGatheringPolicy)nativePolicy {
444  switch (nativePolicy) {
445    case webrtc::PeerConnectionInterface::GATHER_ONCE:
446      return RTCContinualGatheringPolicyGatherOnce;
447    case webrtc::PeerConnectionInterface::GATHER_CONTINUALLY:
448      return RTCContinualGatheringPolicyGatherContinually;
449  }
450}
451
452+ (NSString *)stringForContinualGatheringPolicy:
453    (RTCContinualGatheringPolicy)policy {
454  switch (policy) {
455    case RTCContinualGatheringPolicyGatherOnce:
456      return @"GATHER_ONCE";
457    case RTCContinualGatheringPolicyGatherContinually:
458      return @"GATHER_CONTINUALLY";
459  }
460}
461
462+ (rtc::KeyType)nativeEncryptionKeyTypeForKeyType:
463    (RTCEncryptionKeyType)keyType {
464  switch (keyType) {
465    case RTCEncryptionKeyTypeRSA:
466      return rtc::KT_RSA;
467    case RTCEncryptionKeyTypeECDSA:
468      return rtc::KT_ECDSA;
469  }
470}
471
472+ (webrtc::SdpSemantics)nativeSdpSemanticsForSdpSemantics:(RTCSdpSemantics)sdpSemantics {
473  switch (sdpSemantics) {
474    case RTCSdpSemanticsPlanB:
475      return webrtc::SdpSemantics::kPlanB;
476    case RTCSdpSemanticsUnifiedPlan:
477      return webrtc::SdpSemantics::kUnifiedPlan;
478  }
479}
480
481+ (RTCSdpSemantics)sdpSemanticsForNativeSdpSemantics:(webrtc::SdpSemantics)sdpSemantics {
482  switch (sdpSemantics) {
483    case webrtc::SdpSemantics::kPlanB:
484      return RTCSdpSemanticsPlanB;
485    case webrtc::SdpSemantics::kUnifiedPlan:
486      return RTCSdpSemanticsUnifiedPlan;
487  }
488}
489
490+ (NSString *)stringForSdpSemantics:(RTCSdpSemantics)sdpSemantics {
491  switch (sdpSemantics) {
492    case RTCSdpSemanticsPlanB:
493      return @"PLAN_B";
494    case RTCSdpSemanticsUnifiedPlan:
495      return @"UNIFIED_PLAN";
496  }
497}
498
499@end
500