Add new PeerConnection APIs to the ObjC SDK

This CL adds wrappers for the following PeerConnection native
APIs to the Objective C API:
- SdpSemantics enum added to the RTCConfiguration
- RTCRtpTransceiver
- RTCPeerConnection.addTrack
- RTCPeerConnection.removeTrack
- RTCPeerConnection.addTransceiver
- RTCPeerConnection.transceivers

Bug: webrtc:8870
Change-Id: I9449df9742a59e90894712dc7749ca30b569d94b
Reviewed-on: https://webrtc-review.googlesource.com/54780
Commit-Queue: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Kári Helgason <kthelgason@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22214}
diff --git a/sdk/objc/Framework/Classes/PeerConnection/RTCConfiguration+Private.h b/sdk/objc/Framework/Classes/PeerConnection/RTCConfiguration+Private.h
index d9a9916..c572d63 100644
--- a/sdk/objc/Framework/Classes/PeerConnection/RTCConfiguration+Private.h
+++ b/sdk/objc/Framework/Classes/PeerConnection/RTCConfiguration+Private.h
@@ -58,6 +58,12 @@
 
 + (rtc::KeyType)nativeEncryptionKeyTypeForKeyType:(RTCEncryptionKeyType)keyType;
 
++ (webrtc::SdpSemantics)nativeSdpSemanticsForSdpSemantics:(RTCSdpSemantics)sdpSemantics;
+
++ (RTCSdpSemantics)sdpSemanticsForNativeSdpSemantics:(webrtc::SdpSemantics)sdpSemantics;
+
++ (NSString *)stringForSdpSemantics:(RTCSdpSemantics)sdpSemantics;
+
 /**
  * RTCConfiguration struct representation of this RTCConfiguration. This is
  * needed to pass to the underlying C++ APIs.
diff --git a/sdk/objc/Framework/Classes/PeerConnection/RTCConfiguration.mm b/sdk/objc/Framework/Classes/PeerConnection/RTCConfiguration.mm
index 92b334b..ceaac86 100644
--- a/sdk/objc/Framework/Classes/PeerConnection/RTCConfiguration.mm
+++ b/sdk/objc/Framework/Classes/PeerConnection/RTCConfiguration.mm
@@ -43,6 +43,7 @@
     _shouldPresumeWritableWhenFullyRelayed;
 @synthesize iceCheckMinInterval = _iceCheckMinInterval;
 @synthesize iceRegatherIntervalRange = _iceRegatherIntervalRange;
+@synthesize sdpSemantics = _sdpSemantics;
 @synthesize turnCustomizer = _turnCustomizer;
 
 - (instancetype)init {
@@ -96,35 +97,38 @@
       _iceRegatherIntervalRange =
           [[RTCIntervalRange alloc] initWithNativeIntervalRange:nativeIntervalRange];
     }
+    _sdpSemantics = [[self class] sdpSemanticsForNativeSdpSemantics:config.sdp_semantics];
     _turnCustomizer = config.turn_customizer;
   }
   return self;
 }
 
 - (NSString *)description {
-  static NSString *formatString = @"RTCConfiguration: "
-      @"{\n%@\n%@\n%@\n%@\n%@\n%@\n%@\n%d\n%d\n%d\n%d\n%d\n%d\n%d\n%@\n%@\n%d\n%d\n}\n";
+  static NSString *formatString =
+      @"RTCConfiguration: "
+      @"{\n%@\n%@\n%@\n%@\n%@\n%@\n%@\n%@\n%d\n%d\n%d\n%d\n%d\n%d\n%d\n%@\n%@\n%d\n%d\n}\n";
 
-  return
-      [NSString stringWithFormat:formatString,
-                    _iceServers,
-                    [[self class] stringForTransportPolicy:_iceTransportPolicy],
-                    [[self class] stringForBundlePolicy:_bundlePolicy],
-                    [[self class] stringForRtcpMuxPolicy:_rtcpMuxPolicy],
-                    [[self class] stringForTcpCandidatePolicy:_tcpCandidatePolicy],
-                    [[self class] stringForCandidateNetworkPolicy:_candidateNetworkPolicy],
-                    [[self class] stringForContinualGatheringPolicy:_continualGatheringPolicy],
-                    _audioJitterBufferMaxPackets,
-                    _audioJitterBufferFastAccelerate,
-                    _iceConnectionReceivingTimeout,
-                    _iceBackupCandidatePairPingInterval,
-                    _iceCandidatePoolSize,
-                    _shouldPruneTurnPorts,
-                    _shouldPresumeWritableWhenFullyRelayed,
-                    _iceCheckMinInterval,
-                    _iceRegatherIntervalRange,
-                    _disableLinkLocalNetworks,
-                    _maxIPv6Networks];
+  return [NSString
+      stringWithFormat:formatString,
+                       _iceServers,
+                       [[self class] stringForTransportPolicy:_iceTransportPolicy],
+                       [[self class] stringForBundlePolicy:_bundlePolicy],
+                       [[self class] stringForRtcpMuxPolicy:_rtcpMuxPolicy],
+                       [[self class] stringForTcpCandidatePolicy:_tcpCandidatePolicy],
+                       [[self class] stringForCandidateNetworkPolicy:_candidateNetworkPolicy],
+                       [[self class] stringForContinualGatheringPolicy:_continualGatheringPolicy],
+                       [[self class] stringForSdpSemantics:_sdpSemantics],
+                       _audioJitterBufferMaxPackets,
+                       _audioJitterBufferFastAccelerate,
+                       _iceConnectionReceivingTimeout,
+                       _iceBackupCandidatePairPingInterval,
+                       _iceCandidatePoolSize,
+                       _shouldPruneTurnPorts,
+                       _shouldPresumeWritableWhenFullyRelayed,
+                       _iceCheckMinInterval,
+                       _iceRegatherIntervalRange,
+                       _disableLinkLocalNetworks,
+                       _maxIPv6Networks];
 }
 
 #pragma mark - Private
@@ -186,6 +190,7 @@
     nativeConfig->ice_regather_interval_range =
         rtc::Optional<rtc::IntervalRange>(*nativeIntervalRange);
   }
+  nativeConfig->sdp_semantics = [[self class] nativeSdpSemanticsForSdpSemantics:_sdpSemantics];
   if (_turnCustomizer) {
     nativeConfig->turn_customizer = _turnCustomizer;
   }
@@ -397,4 +402,37 @@
   }
 }
 
++ (webrtc::SdpSemantics)nativeSdpSemanticsForSdpSemantics:(RTCSdpSemantics)sdpSemantics {
+  switch (sdpSemantics) {
+    case RTCSdpSemanticsDefault:
+      return webrtc::SdpSemantics::kDefault;
+    case RTCSdpSemanticsPlanB:
+      return webrtc::SdpSemantics::kPlanB;
+    case RTCSdpSemanticsUnifiedPlan:
+      return webrtc::SdpSemantics::kUnifiedPlan;
+  }
+}
+
++ (RTCSdpSemantics)sdpSemanticsForNativeSdpSemantics:(webrtc::SdpSemantics)sdpSemantics {
+  switch (sdpSemantics) {
+    case webrtc::SdpSemantics::kDefault:
+      return RTCSdpSemanticsDefault;
+    case webrtc::SdpSemantics::kPlanB:
+      return RTCSdpSemanticsPlanB;
+    case webrtc::SdpSemantics::kUnifiedPlan:
+      return RTCSdpSemanticsUnifiedPlan;
+  }
+}
+
++ (NSString *)stringForSdpSemantics:(RTCSdpSemantics)sdpSemantics {
+  switch (sdpSemantics) {
+    case RTCSdpSemanticsDefault:
+      return @"DEFAULT";
+    case RTCSdpSemanticsPlanB:
+      return @"PLAN_B";
+    case RTCSdpSemanticsUnifiedPlan:
+      return @"UNIFIED_PLAN";
+  }
+}
+
 @end
diff --git a/sdk/objc/Framework/Classes/PeerConnection/RTCPeerConnection+Private.h b/sdk/objc/Framework/Classes/PeerConnection/RTCPeerConnection+Private.h
index e1017f5..4b46dea 100644
--- a/sdk/objc/Framework/Classes/PeerConnection/RTCPeerConnection+Private.h
+++ b/sdk/objc/Framework/Classes/PeerConnection/RTCPeerConnection+Private.h
@@ -33,6 +33,8 @@
 
   void OnRemoveStream(rtc::scoped_refptr<MediaStreamInterface> stream) override;
 
+  void OnTrack(rtc::scoped_refptr<RtpTransceiverInterface> transceiver) override;
+
   void OnDataChannel(
       rtc::scoped_refptr<DataChannelInterface> data_channel) override;
 
diff --git a/sdk/objc/Framework/Classes/PeerConnection/RTCPeerConnection.mm b/sdk/objc/Framework/Classes/PeerConnection/RTCPeerConnection.mm
index 6376ea0..08b9047 100644
--- a/sdk/objc/Framework/Classes/PeerConnection/RTCPeerConnection.mm
+++ b/sdk/objc/Framework/Classes/PeerConnection/RTCPeerConnection.mm
@@ -17,10 +17,12 @@
 #import "RTCLegacyStatsReport+Private.h"
 #import "RTCMediaConstraints+Private.h"
 #import "RTCMediaStream+Private.h"
+#import "RTCMediaStreamTrack+Private.h"
 #import "RTCPeerConnection+Native.h"
 #import "RTCPeerConnectionFactory+Private.h"
 #import "RTCRtpReceiver+Private.h"
 #import "RTCRtpSender+Private.h"
+#import "RTCRtpTransceiver+Private.h"
 #import "RTCSessionDescription+Private.h"
 #import "WebRTC/RTCLogging.h"
 
@@ -144,6 +146,18 @@
                            didRemoveStream:mediaStream];
 }
 
+void PeerConnectionDelegateAdapter::OnTrack(
+    rtc::scoped_refptr<RtpTransceiverInterface> nativeTransceiver) {
+  RTCRtpTransceiver *transceiver =
+      [[RTCRtpTransceiver alloc] initWithNativeRtpTransceiver:nativeTransceiver];
+  RTCPeerConnection *peer_connection = peer_connection_;
+  if ([peer_connection.delegate
+          respondsToSelector:@selector(peerConnection:didStartReceivingOnTransceiver:)]) {
+    [peer_connection.delegate peerConnection:peer_connection
+              didStartReceivingOnTransceiver:transceiver];
+  }
+}
+
 void PeerConnectionDelegateAdapter::OnDataChannel(
     rtc::scoped_refptr<DataChannelInterface> data_channel) {
   RTCDataChannel *dataChannel =
@@ -334,6 +348,65 @@
   [_localStreams removeObject:stream];
 }
 
+- (RTCRtpSender *)addTrack:(RTCMediaStreamTrack *)track
+              streamLabels:(NSArray<NSString *> *)streamLabels {
+  std::vector<std::string> nativeStreamLabels;
+  for (NSString *label in streamLabels) {
+    nativeStreamLabels.push_back([label UTF8String]);
+  }
+  webrtc::RTCErrorOr<rtc::scoped_refptr<webrtc::RtpSenderInterface>> nativeSenderOrError =
+      _peerConnection->AddTrack(track.nativeTrack, nativeStreamLabels);
+  if (!nativeSenderOrError.ok()) {
+    RTCLogError(@"Failed to add track %@: %s", track, nativeSenderOrError.error().message());
+    return nil;
+  }
+  return [[RTCRtpSender alloc] initWithNativeRtpSender:nativeSenderOrError.MoveValue()];
+}
+
+- (BOOL)removeTrack:(RTCRtpSender *)sender {
+  bool result = _peerConnection->RemoveTrack(sender.nativeRtpSender);
+  if (!result) {
+    RTCLogError(@"Failed to remote track %@", sender);
+  }
+  return result;
+}
+
+- (RTCRtpTransceiver *)addTransceiverWithTrack:(RTCMediaStreamTrack *)track {
+  return [self addTransceiverWithTrack:track init:[[RTCRtpTransceiverInit alloc] init]];
+}
+
+- (RTCRtpTransceiver *)addTransceiverWithTrack:(RTCMediaStreamTrack *)track
+                                          init:(RTCRtpTransceiverInit *)init {
+  webrtc::RTCErrorOr<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>> nativeTransceiverOrError =
+      _peerConnection->AddTransceiver(track.nativeTrack, init.nativeInit);
+  if (!nativeTransceiverOrError.ok()) {
+    RTCLogError(
+        @"Failed to add transceiver %@: %s", track, nativeTransceiverOrError.error().message());
+    return nil;
+  }
+  return
+      [[RTCRtpTransceiver alloc] initWithNativeRtpTransceiver:nativeTransceiverOrError.MoveValue()];
+}
+
+- (RTCRtpTransceiver *)addTransceiverOfType:(RTCRtpMediaType)mediaType {
+  return [self addTransceiverOfType:mediaType init:[[RTCRtpTransceiverInit alloc] init]];
+}
+
+- (RTCRtpTransceiver *)addTransceiverOfType:(RTCRtpMediaType)mediaType
+                                       init:(RTCRtpTransceiverInit *)init {
+  webrtc::RTCErrorOr<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>> nativeTransceiverOrError =
+      _peerConnection->AddTransceiver([RTCRtpReceiver nativeMediaTypeForMediaType:mediaType],
+                                      init.nativeInit);
+  if (!nativeTransceiverOrError.ok()) {
+    RTCLogError(@"Failed to add transceiver %@: %s",
+                [RTCRtpReceiver stringForMediaType:mediaType],
+                nativeTransceiverOrError.error().message());
+    return nil;
+  }
+  return
+      [[RTCRtpTransceiver alloc] initWithNativeRtpTransceiver:nativeTransceiverOrError.MoveValue()];
+}
+
 - (void)offerForConstraints:(RTCMediaConstraints *)constraints
           completionHandler:
     (void (^)(RTCSessionDescription *sessionDescription,
@@ -451,6 +524,18 @@
   return receivers;
 }
 
+- (NSArray<RTCRtpTransceiver *> *)transceivers {
+  std::vector<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>> nativeTransceivers(
+      _peerConnection->GetTransceivers());
+  NSMutableArray *transceivers = [[NSMutableArray alloc] init];
+  for (auto nativeTransceiver : nativeTransceivers) {
+    RTCRtpTransceiver *transceiver =
+        [[RTCRtpTransceiver alloc] initWithNativeRtpTransceiver:nativeTransceiver];
+    [transceivers addObject:transceiver];
+  }
+  return transceivers;
+}
+
 #pragma mark - Private
 
 + (webrtc::PeerConnectionInterface::SignalingState)nativeSignalingStateForState:
diff --git a/sdk/objc/Framework/Classes/PeerConnection/RTCRtpReceiver+Private.h b/sdk/objc/Framework/Classes/PeerConnection/RTCRtpReceiver+Private.h
index 9873811..63bdfa3 100644
--- a/sdk/objc/Framework/Classes/PeerConnection/RTCRtpReceiver+Private.h
+++ b/sdk/objc/Framework/Classes/PeerConnection/RTCRtpReceiver+Private.h
@@ -40,6 +40,10 @@
 
 + (RTCRtpMediaType)mediaTypeForNativeMediaType:(cricket::MediaType)nativeMediaType;
 
++ (cricket::MediaType)nativeMediaTypeForMediaType:(RTCRtpMediaType)mediaType;
+
++ (NSString*)stringForMediaType:(RTCRtpMediaType)mediaType;
+
 @end
 
 NS_ASSUME_NONNULL_END
diff --git a/sdk/objc/Framework/Classes/PeerConnection/RTCRtpReceiver.mm b/sdk/objc/Framework/Classes/PeerConnection/RTCRtpReceiver.mm
index 12f3ab6..b1f5be9 100644
--- a/sdk/objc/Framework/Classes/PeerConnection/RTCRtpReceiver.mm
+++ b/sdk/objc/Framework/Classes/PeerConnection/RTCRtpReceiver.mm
@@ -126,4 +126,26 @@
   }
 }
 
++ (cricket::MediaType)nativeMediaTypeForMediaType:(RTCRtpMediaType)mediaType {
+  switch (mediaType) {
+    case RTCRtpMediaTypeAudio:
+      return cricket::MEDIA_TYPE_AUDIO;
+    case RTCRtpMediaTypeVideo:
+      return cricket::MEDIA_TYPE_VIDEO;
+    case RTCRtpMediaTypeData:
+      return cricket::MEDIA_TYPE_DATA;
+  }
+}
+
++ (NSString *)stringForMediaType:(RTCRtpMediaType)mediaType {
+  switch (mediaType) {
+    case RTCRtpMediaTypeAudio:
+      return @"AUDIO";
+    case RTCRtpMediaTypeVideo:
+      return @"VIDEO";
+    case RTCRtpMediaTypeData:
+      return @"DATA";
+  }
+}
+
 @end
diff --git a/sdk/objc/Framework/Classes/PeerConnection/RTCRtpTransceiver+Private.h b/sdk/objc/Framework/Classes/PeerConnection/RTCRtpTransceiver+Private.h
new file mode 100644
index 0000000..cc1f4fe
--- /dev/null
+++ b/sdk/objc/Framework/Classes/PeerConnection/RTCRtpTransceiver+Private.h
@@ -0,0 +1,41 @@
+/*
+ *  Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#import "WebRTC/RTCRtpTransceiver.h"
+
+#include "api/rtptransceiverinterface.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface RTCRtpTransceiverInit ()
+
+@property(nonatomic, readonly) webrtc::RtpTransceiverInit nativeInit;
+
+@end
+
+@interface RTCRtpTransceiver ()
+
+@property(nonatomic, readonly) rtc::scoped_refptr<webrtc::RtpTransceiverInterface>
+    nativeRtpTransceiver;
+
+/** Initialize an RTCRtpTransceiver with a native RtpTransceiverInterface. */
+- (instancetype)initWithNativeRtpTransceiver:
+        (rtc::scoped_refptr<webrtc::RtpTransceiverInterface>)nativeRtpTransceiver
+    NS_DESIGNATED_INITIALIZER;
+
++ (webrtc::RtpTransceiverDirection)nativeRtpTransceiverDirectionFromDirection:
+        (RTCRtpTransceiverDirection)direction;
+
++ (RTCRtpTransceiverDirection)rtpTransceiverDirectionFromNativeDirection:
+        (webrtc::RtpTransceiverDirection)nativeDirection;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/sdk/objc/Framework/Classes/PeerConnection/RTCRtpTransceiver.mm b/sdk/objc/Framework/Classes/PeerConnection/RTCRtpTransceiver.mm
new file mode 100644
index 0000000..ce182bc
--- /dev/null
+++ b/sdk/objc/Framework/Classes/PeerConnection/RTCRtpTransceiver.mm
@@ -0,0 +1,163 @@
+/*
+ *  Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#import "RTCRtpTransceiver+Private.h"
+
+#import "NSString+StdString.h"
+#import "RTCRtpEncodingParameters+Private.h"
+#import "RTCRtpParameters+Private.h"
+#import "RTCRtpReceiver+Private.h"
+#import "RTCRtpSender+Private.h"
+#import "WebRTC/RTCLogging.h"
+
+@implementation RTCRtpTransceiverInit
+
+@synthesize direction = _direction;
+@synthesize streamLabels = _streamLabels;
+@synthesize sendEncodings = _sendEncodings;
+
+- (instancetype)init {
+  if (self = [super init]) {
+    _direction = RTCRtpTransceiverDirectionSendRecv;
+  }
+  return self;
+}
+
+- (webrtc::RtpTransceiverInit)nativeInit {
+  webrtc::RtpTransceiverInit init;
+  init.direction = [RTCRtpTransceiver nativeRtpTransceiverDirectionFromDirection:_direction];
+  for (NSString *streamLabel in _streamLabels) {
+    init.stream_labels.push_back([streamLabel UTF8String]);
+  }
+  for (RTCRtpEncodingParameters *sendEncoding in _sendEncodings) {
+    init.send_encodings.push_back(sendEncoding.nativeParameters);
+  }
+  return init;
+}
+
+@end
+
+@implementation RTCRtpTransceiver {
+  rtc::scoped_refptr<webrtc::RtpTransceiverInterface> _nativeRtpTransceiver;
+}
+
+- (RTCRtpMediaType)mediaType {
+  return [RTCRtpReceiver mediaTypeForNativeMediaType:_nativeRtpTransceiver->media_type()];
+}
+
+- (NSString *)mid {
+  if (_nativeRtpTransceiver->mid()) {
+    return [NSString stringForStdString:*_nativeRtpTransceiver->mid()];
+  } else {
+    return nil;
+  }
+}
+
+@synthesize sender = _sender;
+@synthesize receiver = _receiver;
+
+- (BOOL)isStopped {
+  return _nativeRtpTransceiver->stopped();
+}
+
+- (RTCRtpTransceiverDirection)direction {
+  return [RTCRtpTransceiver
+      rtpTransceiverDirectionFromNativeDirection:_nativeRtpTransceiver->direction()];
+}
+
+- (void)setDirection:(RTCRtpTransceiverDirection)direction {
+  _nativeRtpTransceiver->SetDirection(
+      [RTCRtpTransceiver nativeRtpTransceiverDirectionFromDirection:direction]);
+}
+
+- (BOOL)currentDirection:(RTCRtpTransceiverDirection *)currentDirectionOut {
+  if (_nativeRtpTransceiver->current_direction()) {
+    *currentDirectionOut = [RTCRtpTransceiver
+        rtpTransceiverDirectionFromNativeDirection:*_nativeRtpTransceiver->current_direction()];
+    return YES;
+  } else {
+    return NO;
+  }
+}
+
+- (void)stop {
+  _nativeRtpTransceiver->Stop();
+}
+
+- (NSString *)description {
+  return [NSString
+      stringWithFormat:@"RTCRtpTransceiver {\n  sender: %@\n  receiver: %@\n}", _sender, _receiver];
+}
+
+- (BOOL)isEqual:(id)object {
+  if (self == object) {
+    return YES;
+  }
+  if (object == nil) {
+    return NO;
+  }
+  if (![object isMemberOfClass:[self class]]) {
+    return NO;
+  }
+  RTCRtpTransceiver *transceiver = (RTCRtpTransceiver *)object;
+  return _nativeRtpTransceiver == transceiver.nativeRtpTransceiver;
+}
+
+- (NSUInteger)hash {
+  return (NSUInteger)_nativeRtpTransceiver.get();
+}
+
+#pragma mark - Private
+
+- (rtc::scoped_refptr<webrtc::RtpTransceiverInterface>)nativeRtpTransceiver {
+  return _nativeRtpTransceiver;
+}
+
+- (instancetype)initWithNativeRtpTransceiver:
+        (rtc::scoped_refptr<webrtc::RtpTransceiverInterface>)nativeRtpTransceiver {
+  NSParameterAssert(nativeRtpTransceiver);
+  if (self = [super init]) {
+    _nativeRtpTransceiver = nativeRtpTransceiver;
+    _sender = [[RTCRtpSender alloc] initWithNativeRtpSender:nativeRtpTransceiver->sender()];
+    _receiver = [[RTCRtpReceiver alloc] initWithNativeRtpReceiver:nativeRtpTransceiver->receiver()];
+    RTCLogInfo(@"RTCRtpTransceiver(%p): created transceiver: %@", self, self.description);
+  }
+  return self;
+}
+
++ (webrtc::RtpTransceiverDirection)nativeRtpTransceiverDirectionFromDirection:
+        (RTCRtpTransceiverDirection)direction {
+  switch (direction) {
+    case RTCRtpTransceiverDirectionSendRecv:
+      return webrtc::RtpTransceiverDirection::kSendRecv;
+    case RTCRtpTransceiverDirectionSendOnly:
+      return webrtc::RtpTransceiverDirection::kSendOnly;
+    case RTCRtpTransceiverDirectionRecvOnly:
+      return webrtc::RtpTransceiverDirection::kRecvOnly;
+    case RTCRtpTransceiverDirectionInactive:
+      return webrtc::RtpTransceiverDirection::kInactive;
+  }
+}
+
++ (RTCRtpTransceiverDirection)rtpTransceiverDirectionFromNativeDirection:
+        (webrtc::RtpTransceiverDirection)nativeDirection {
+  switch (nativeDirection) {
+    case webrtc::RtpTransceiverDirection::kSendRecv:
+      return RTCRtpTransceiverDirectionSendRecv;
+    case webrtc::RtpTransceiverDirection::kSendOnly:
+      return RTCRtpTransceiverDirectionSendOnly;
+    case webrtc::RtpTransceiverDirection::kRecvOnly:
+      return RTCRtpTransceiverDirectionRecvOnly;
+    case webrtc::RtpTransceiverDirection::kInactive:
+      return RTCRtpTransceiverDirectionInactive;
+  }
+}
+
+@end