Remove cricket::RtpTransceiverDirection

Replaces cricket::RtpTransceiverDirection with
webrtc::RtpTransceiverDirection, which is part of the public API.

Bug: webrtc:8558
Change-Id: Ibfc9373e25187e98fb969e7ac937a1371c8fa4c7
Reviewed-on: https://webrtc-review.googlesource.com/24129
Commit-Queue: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Peter Thatcher <pthatcher@webrtc.org>
Reviewed-by: Zhi Huang <zhihuang@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20899}
diff --git a/pc/BUILD.gn b/pc/BUILD.gn
index da30976..165be56 100644
--- a/pc/BUILD.gn
+++ b/pc/BUILD.gn
@@ -48,6 +48,8 @@
     "mediasession.h",
     "rtcpmuxfilter.cc",
     "rtcpmuxfilter.h",
+    "rtpmediautils.cc",
+    "rtpmediautils.h",
     "rtptransport.cc",
     "rtptransport.h",
     "rtptransportinternal.h",
@@ -400,6 +402,7 @@
       "proxy_unittest.cc",
       "rtcstats_integrationtest.cc",
       "rtcstatscollector_unittest.cc",
+      "rtpmediautils_unittest.cc",
       "rtpsenderreceiver_unittest.cc",
       "sctputils_unittest.cc",
       "statscollector_unittest.cc",
diff --git a/pc/mediasession.cc b/pc/mediasession.cc
index e9b4c73..9c31f50 100644
--- a/pc/mediasession.cc
+++ b/pc/mediasession.cc
@@ -25,6 +25,7 @@
 #include "media/base/mediaconstants.h"
 #include "p2p/base/p2pconstants.h"
 #include "pc/channelmanager.h"
+#include "pc/rtpmediautils.h"
 #include "pc/srtpfilter.h"
 #include "rtc_base/base64.h"
 #include "rtc_base/checks.h"
@@ -33,6 +34,9 @@
 #include "rtc_base/stringutils.h"
 
 namespace {
+
+using webrtc::RtpTransceiverDirection;
+
 const char kInline[] = "inline:";
 
 void GetSupportedSdesCryptoSuiteNames(void (*func)(const rtc::CryptoOptions&,
@@ -97,30 +101,47 @@
   return IsPlainSctp(protocol) || IsDtlsSctp(protocol);
 }
 
-RtpTransceiverDirection RtpTransceiverDirection::FromMediaContentDirection(
-    MediaContentDirection md) {
-  const bool send = (md == MD_SENDRECV || md == MD_SENDONLY);
-  const bool recv = (md == MD_SENDRECV || md == MD_RECVONLY);
-  return RtpTransceiverDirection(send, recv);
+RtpTransceiverDirection RtpTransceiverDirectionFromMediaContentDirection(
+    MediaContentDirection direction) {
+  switch (direction) {
+    case MD_SENDRECV:
+      return RtpTransceiverDirection::kSendRecv;
+    case MD_SENDONLY:
+      return RtpTransceiverDirection::kSendOnly;
+    case MD_RECVONLY:
+      return RtpTransceiverDirection::kRecvOnly;
+    case MD_INACTIVE:
+      return RtpTransceiverDirection::kInactive;
+  }
+  RTC_NOTREACHED();
+  return RtpTransceiverDirection::kInactive;
 }
 
-MediaContentDirection RtpTransceiverDirection::ToMediaContentDirection() const {
-  if (send && recv) {
-    return MD_SENDRECV;
-  } else if (send) {
-    return MD_SENDONLY;
-  } else if (recv) {
-    return MD_RECVONLY;
+MediaContentDirection MediaContentDirectionFromRtpTransceiverDirection(
+    RtpTransceiverDirection direction) {
+  switch (direction) {
+    case RtpTransceiverDirection::kSendRecv:
+      return MD_SENDRECV;
+    case RtpTransceiverDirection::kSendOnly:
+      return MD_SENDONLY;
+    case RtpTransceiverDirection::kRecvOnly:
+      return MD_RECVONLY;
+    case RtpTransceiverDirection::kInactive:
+      return MD_INACTIVE;
   }
-
+  RTC_NOTREACHED();
   return MD_INACTIVE;
 }
 
-RtpTransceiverDirection
-NegotiateRtpTransceiverDirection(RtpTransceiverDirection offer,
-                                 RtpTransceiverDirection wants) {
-  return RtpTransceiverDirection(offer.recv && wants.send,
-                                 offer.send && wants.recv);
+static RtpTransceiverDirection NegotiateRtpTransceiverDirection(
+    RtpTransceiverDirection offer,
+    RtpTransceiverDirection wants) {
+  bool offer_send = webrtc::RtpTransceiverDirectionHasSend(offer);
+  bool offer_recv = webrtc::RtpTransceiverDirectionHasRecv(offer);
+  bool wants_send = webrtc::RtpTransceiverDirectionHasSend(wants);
+  bool wants_recv = webrtc::RtpTransceiverDirectionHasRecv(wants);
+  return webrtc::RtpTransceiverDirectionFromSendRecv(offer_recv && wants_send,
+                                                     offer_send && wants_recv);
 }
 
 static bool IsMediaContentOfType(const ContentInfo* content,
@@ -1156,11 +1177,12 @@
   }
 
   auto offer_rtd =
-      RtpTransceiverDirection::FromMediaContentDirection(offer->direction());
+      RtpTransceiverDirectionFromMediaContentDirection(offer->direction());
 
-  answer->set_direction(NegotiateRtpTransceiverDirection(
-                            offer_rtd, media_description_options.direction)
-                            .ToMediaContentDirection());
+  answer->set_direction(
+      cricket::MediaContentDirectionFromRtpTransceiverDirection(
+          NegotiateRtpTransceiverDirection(
+              offer_rtd, media_description_options.direction)));
   return true;
 }
 
@@ -1569,34 +1591,37 @@
 
 const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForOffer(
     const RtpTransceiverDirection& direction) const {
-  // If stream is inactive - generate list as if sendrecv.
-  if (direction.send == direction.recv) {
-    return audio_sendrecv_codecs_;
-  } else if (direction.send) {
-    return audio_send_codecs_;
-  } else {
-    return audio_recv_codecs_;
+  switch (direction) {
+    // If stream is inactive - generate list as if sendrecv.
+    case RtpTransceiverDirection::kSendRecv:
+    case RtpTransceiverDirection::kInactive:
+      return audio_sendrecv_codecs_;
+    case RtpTransceiverDirection::kSendOnly:
+      return audio_send_codecs_;
+    case RtpTransceiverDirection::kRecvOnly:
+      return audio_recv_codecs_;
   }
+  RTC_NOTREACHED();
+  return audio_sendrecv_codecs_;
 }
 
 const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForAnswer(
     const RtpTransceiverDirection& offer,
     const RtpTransceiverDirection& answer) const {
-  // For inactive and sendrecv answers, generate lists as if we were to accept
-  // the offer's direction. See RFC 3264 Section 6.1.
-  if (answer.send == answer.recv) {
-    if (offer.send == offer.recv) {
-      return audio_sendrecv_codecs_;
-    } else if (offer.send) {
-      return audio_recv_codecs_;
-    } else {
+  switch (answer) {
+    // For inactive and sendrecv answers, generate lists as if we were to accept
+    // the offer's direction. See RFC 3264 Section 6.1.
+    case RtpTransceiverDirection::kSendRecv:
+    case RtpTransceiverDirection::kInactive:
+      return GetAudioCodecsForOffer(
+          webrtc::RtpTransceiverDirectionReversed(offer));
+    case RtpTransceiverDirection::kSendOnly:
       return audio_send_codecs_;
-    }
-  } else if (answer.send) {
-    return audio_send_codecs_;
-  } else {
-    return audio_recv_codecs_;
+    case RtpTransceiverDirection::kRecvOnly:
+      return audio_recv_codecs_;
   }
+  RTC_NOTREACHED();
+  return audio_sendrecv_codecs_;
 }
 
 void MergeCodecsFromDescription(const SessionDescription* description,
@@ -1908,7 +1933,8 @@
   SetMediaProtocol(secure_transport, audio.get());
 
   audio->set_direction(
-      media_description_options.direction.ToMediaContentDirection());
+      cricket::MediaContentDirectionFromRtpTransceiverDirection(
+          media_description_options.direction));
 
   desc->AddContent(media_description_options.mid, NS_JINGLE_RTP,
                    media_description_options.stopped, audio.release());
@@ -1979,7 +2005,8 @@
   SetMediaProtocol(secure_transport, video.get());
 
   video->set_direction(
-      media_description_options.direction.ToMediaContentDirection());
+      cricket::MediaContentDirectionFromRtpTransceiverDirection(
+          media_description_options.direction));
 
   desc->AddContent(media_description_options.mid, NS_JINGLE_RTP,
                    media_description_options.stopped, video.release());
@@ -2098,7 +2125,7 @@
   // and the selected direction in the answer.
   // Note these will be filtered one final time in CreateMediaContentAnswer.
   auto wants_rtd = media_description_options.direction;
-  auto offer_rtd = RtpTransceiverDirection::FromMediaContentDirection(
+  auto offer_rtd = RtpTransceiverDirectionFromMediaContentDirection(
       offer_audio_description->direction());
   auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);
   AudioCodecs supported_audio_codecs =
diff --git a/pc/mediasession.h b/pc/mediasession.h
index 2277000..41e867b 100644
--- a/pc/mediasession.h
+++ b/pc/mediasession.h
@@ -20,13 +20,14 @@
 
 #include "api/cryptoparams.h"
 #include "api/mediatypes.h"
+#include "api/rtptransceiverinterface.h"
 #include "media/base/codec.h"
 #include "media/base/mediachannel.h"
 #include "media/base/mediaconstants.h"
 #include "media/base/mediaengine.h"  // For DataChannelType
 #include "media/base/streamparams.h"
-#include "p2p/base/sessiondescription.h"
 #include "p2p/base/jseptransport.h"
+#include "p2p/base/sessiondescription.h"
 #include "p2p/base/transportdescriptionfactory.h"
 
 namespace cricket {
@@ -73,33 +74,11 @@
 // Default RTCP CNAME for unit tests.
 const char kDefaultRtcpCname[] = "DefaultRtcpCname";
 
-struct RtpTransceiverDirection {
-  bool send;
-  bool recv;
-
-  RtpTransceiverDirection(bool send, bool recv) : send(send), recv(recv) {}
-
-  bool operator==(const RtpTransceiverDirection& o) const {
-    return send == o.send && recv == o.recv;
-  }
-
-  bool operator!=(const RtpTransceiverDirection& o) const {
-    return !(*this == o);
-  }
-
-  static RtpTransceiverDirection FromMediaContentDirection(
-      MediaContentDirection md);
-
-  MediaContentDirection ToMediaContentDirection() const;
-
-  RtpTransceiverDirection Reversed() const {
-    return RtpTransceiverDirection(recv, send);
-  }
-};
-
-RtpTransceiverDirection
-NegotiateRtpTransceiverDirection(RtpTransceiverDirection offer,
-                                 RtpTransceiverDirection wants);
+webrtc::RtpTransceiverDirection
+RtpTransceiverDirectionFromMediaContentDirection(
+    MediaContentDirection direction);
+MediaContentDirection MediaContentDirectionFromRtpTransceiverDirection(
+    webrtc::RtpTransceiverDirection direction);
 
 // Options for an RtpSender contained with an media description/"m=" section.
 struct SenderOptions {
@@ -114,7 +93,7 @@
 struct MediaDescriptionOptions {
   MediaDescriptionOptions(MediaType type,
                           const std::string& mid,
-                          RtpTransceiverDirection direction,
+                          webrtc::RtpTransceiverDirection direction,
                           bool stopped)
       : type(type), mid(mid), direction(direction), stopped(stopped) {}
 
@@ -132,7 +111,7 @@
 
   MediaType type;
   std::string mid;
-  RtpTransceiverDirection direction;
+  webrtc::RtpTransceiverDirection direction;
   bool stopped;
   TransportOptions transport_options;
   // Note: There's no equivalent "RtpReceiverOptions" because only send
@@ -467,10 +446,10 @@
 
  private:
   const AudioCodecs& GetAudioCodecsForOffer(
-      const RtpTransceiverDirection& direction) const;
+      const webrtc::RtpTransceiverDirection& direction) const;
   const AudioCodecs& GetAudioCodecsForAnswer(
-      const RtpTransceiverDirection& offer,
-      const RtpTransceiverDirection& answer) const;
+      const webrtc::RtpTransceiverDirection& offer,
+      const webrtc::RtpTransceiverDirection& answer) const;
   void GetCodecsForOffer(const SessionDescription* current_description,
                          AudioCodecs* audio_codecs,
                          VideoCodecs* video_codecs,
diff --git a/pc/mediasession_unittest.cc b/pc/mediasession_unittest.cc
index 09e8684..c2653df 100644
--- a/pc/mediasession_unittest.cc
+++ b/pc/mediasession_unittest.cc
@@ -18,6 +18,7 @@
 #include "p2p/base/transportdescription.h"
 #include "p2p/base/transportinfo.h"
 #include "pc/mediasession.h"
+#include "pc/rtpmediautils.h"
 #include "pc/srtpfilter.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/fakesslidentity.h"
@@ -66,7 +67,6 @@
 using cricket::SEC_DISABLED;
 using cricket::SEC_ENABLED;
 using cricket::SEC_REQUIRED;
-using cricket::RtpTransceiverDirection;
 using rtc::CS_AES_CM_128_HMAC_SHA1_32;
 using rtc::CS_AES_CM_128_HMAC_SHA1_80;
 using rtc::CS_AEAD_AES_128_GCM;
@@ -276,7 +276,7 @@
                             MediaSessionOptions* opts) {
   opts->media_description_options.push_back(MediaDescriptionOptions(
       type, mid,
-      cricket::RtpTransceiverDirection::FromMediaContentDirection(direction),
+      cricket::RtpTransceiverDirectionFromMediaContentDirection(direction),
       stopped));
 }
 
@@ -3608,7 +3608,7 @@
   MediaSessionOptions opts;
   AddMediaSection(MEDIA_TYPE_AUDIO, "audio", direction, kActive, &opts);
 
-  if (RtpTransceiverDirection::FromMediaContentDirection(direction).send) {
+  if (direction == cricket::MD_SENDRECV || direction == cricket::MD_SENDONLY) {
     AttachSenderToMediaSection("audio", MEDIA_TYPE_AUDIO, kAudioTrack1,
                                {kMediaStream1}, 1, &opts);
   }
@@ -3707,8 +3707,8 @@
   AddMediaSection(MEDIA_TYPE_AUDIO, "audio", offer_direction, kActive,
                   &offer_opts);
 
-  if (RtpTransceiverDirection::FromMediaContentDirection(offer_direction)
-          .send) {
+  if (webrtc::RtpTransceiverDirectionHasSend(
+          RtpTransceiverDirectionFromMediaContentDirection(offer_direction))) {
     AttachSenderToMediaSection("audio", MEDIA_TYPE_AUDIO, kAudioTrack1,
                                {kMediaStream1}, 1, &offer_opts);
   }
@@ -3721,8 +3721,8 @@
   AddMediaSection(MEDIA_TYPE_AUDIO, "audio", answer_direction, kActive,
                   &answer_opts);
 
-  if (RtpTransceiverDirection::FromMediaContentDirection(answer_direction)
-          .send) {
+  if (webrtc::RtpTransceiverDirectionHasSend(
+          RtpTransceiverDirectionFromMediaContentDirection(answer_direction))) {
     AttachSenderToMediaSection("audio", MEDIA_TYPE_AUDIO, kAudioTrack1,
                                {kMediaStream1}, 1, &answer_opts);
   }
diff --git a/pc/peerconnection.cc b/pc/peerconnection.cc
index 2c6cc4b..0011ad7 100644
--- a/pc/peerconnection.cc
+++ b/pc/peerconnection.cc
@@ -31,6 +31,7 @@
 #include "pc/mediastream.h"
 #include "pc/mediastreamobserver.h"
 #include "pc/remoteaudiosource.h"
+#include "pc/rtpmediautils.h"
 #include "pc/rtpreceiver.h"
 #include "pc/rtpsender.h"
 #include "pc/sctputils.h"
@@ -2527,9 +2528,9 @@
   if (local_description()) {
     GenerateMediaDescriptionOptions(
         local_description(),
-        cricket::RtpTransceiverDirection(send_audio, recv_audio),
-        cricket::RtpTransceiverDirection(send_video, recv_video), &audio_index,
-        &video_index, &data_index, session_options);
+        RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio),
+        RtpTransceiverDirectionFromSendRecv(send_video, recv_video),
+        &audio_index, &video_index, &data_index, session_options);
   }
 
   // Add audio/video/data m= sections to the end if needed.
@@ -2537,21 +2538,23 @@
     session_options->media_description_options.push_back(
         cricket::MediaDescriptionOptions(
             cricket::MEDIA_TYPE_AUDIO, cricket::CN_AUDIO,
-            cricket::RtpTransceiverDirection(send_audio, recv_audio), false));
+            RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio),
+            false));
     audio_index = session_options->media_description_options.size() - 1;
   }
   if (!video_index && offer_new_video_description) {
     session_options->media_description_options.push_back(
         cricket::MediaDescriptionOptions(
             cricket::MEDIA_TYPE_VIDEO, cricket::CN_VIDEO,
-            cricket::RtpTransceiverDirection(send_video, recv_video), false));
+            RtpTransceiverDirectionFromSendRecv(send_video, recv_video),
+            false));
     video_index = session_options->media_description_options.size() - 1;
   }
   if (!data_index && offer_new_data_description) {
     session_options->media_description_options.push_back(
         cricket::MediaDescriptionOptions(
             cricket::MEDIA_TYPE_DATA, cricket::CN_DATA,
-            cricket::RtpTransceiverDirection(true, true), false));
+            RtpTransceiverDirection::kSendRecv, false));
     data_index = session_options->media_description_options.size() - 1;
   }
 
@@ -2623,9 +2626,9 @@
     // direction with the offered direction.
     GenerateMediaDescriptionOptions(
         remote_description(),
-        cricket::RtpTransceiverDirection(send_audio, recv_audio),
-        cricket::RtpTransceiverDirection(send_video, recv_video), &audio_index,
-        &video_index, &data_index, session_options);
+        RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio),
+        RtpTransceiverDirectionFromSendRecv(send_video, recv_video),
+        &audio_index, &video_index, &data_index, session_options);
   }
 
   cricket::MediaDescriptionOptions* audio_media_description_options =
@@ -2662,8 +2665,8 @@
 
 void PeerConnection::GenerateMediaDescriptionOptions(
     const SessionDescriptionInterface* session_desc,
-    cricket::RtpTransceiverDirection audio_direction,
-    cricket::RtpTransceiverDirection video_direction,
+    RtpTransceiverDirection audio_direction,
+    RtpTransceiverDirection video_direction,
     rtc::Optional<size_t>* audio_index,
     rtc::Optional<size_t>* video_index,
     rtc::Optional<size_t>* data_index,
@@ -2676,12 +2679,12 @@
         session_options->media_description_options.push_back(
             cricket::MediaDescriptionOptions(
                 cricket::MEDIA_TYPE_AUDIO, content.name,
-                cricket::RtpTransceiverDirection(false, false), true));
+                RtpTransceiverDirection::kInactive, true));
       } else {
         session_options->media_description_options.push_back(
             cricket::MediaDescriptionOptions(
                 cricket::MEDIA_TYPE_AUDIO, content.name, audio_direction,
-                !audio_direction.send && !audio_direction.recv));
+                audio_direction == RtpTransceiverDirection::kInactive));
         *audio_index = session_options->media_description_options.size() - 1;
       }
     } else if (IsVideoContent(&content)) {
@@ -2690,12 +2693,12 @@
         session_options->media_description_options.push_back(
             cricket::MediaDescriptionOptions(
                 cricket::MEDIA_TYPE_VIDEO, content.name,
-                cricket::RtpTransceiverDirection(false, false), true));
+                RtpTransceiverDirection::kInactive, true));
       } else {
         session_options->media_description_options.push_back(
             cricket::MediaDescriptionOptions(
                 cricket::MEDIA_TYPE_VIDEO, content.name, video_direction,
-                !video_direction.send && !video_direction.recv));
+                video_direction == RtpTransceiverDirection::kInactive));
         *video_index = session_options->media_description_options.size() - 1;
       }
     } else {
@@ -2705,14 +2708,14 @@
         session_options->media_description_options.push_back(
             cricket::MediaDescriptionOptions(
                 cricket::MEDIA_TYPE_DATA, content.name,
-                cricket::RtpTransceiverDirection(false, false), true));
+                RtpTransceiverDirection::kInactive, true));
       } else {
         session_options->media_description_options.push_back(
             cricket::MediaDescriptionOptions(
                 cricket::MEDIA_TYPE_DATA, content.name,
                 // Direction for data sections is meaningless, but legacy
                 // endpoints might expect sendrecv.
-                cricket::RtpTransceiverDirection(true, true), false));
+                RtpTransceiverDirection::kSendRecv, false));
         *data_index = session_options->media_description_options.size() - 1;
       }
     }
diff --git a/pc/peerconnection.h b/pc/peerconnection.h
index d262ede..0d7f5c9c 100644
--- a/pc/peerconnection.h
+++ b/pc/peerconnection.h
@@ -394,8 +394,8 @@
   // local description or remote description.
   void GenerateMediaDescriptionOptions(
       const SessionDescriptionInterface* session_desc,
-      cricket::RtpTransceiverDirection audio_direction,
-      cricket::RtpTransceiverDirection video_direction,
+      RtpTransceiverDirection audio_direction,
+      RtpTransceiverDirection video_direction,
       rtc::Optional<size_t>* audio_index,
       rtc::Optional<size_t>* video_index,
       rtc::Optional<size_t>* data_index,
diff --git a/pc/peerconnection_media_unittest.cc b/pc/peerconnection_media_unittest.cc
index 20690d2..9f4da02 100644
--- a/pc/peerconnection_media_unittest.cc
+++ b/pc/peerconnection_media_unittest.cc
@@ -20,6 +20,7 @@
 #include "p2p/base/fakeportallocator.h"
 #include "pc/mediasession.h"
 #include "pc/peerconnectionwrapper.h"
+#include "pc/rtpmediautils.h"
 #include "pc/sdputils.h"
 #ifdef WEBRTC_ANDROID
 #include "pc/test/androidtestinitializer.h"
@@ -429,16 +430,18 @@
   // 2. Receive if the answerer has explicitly set the offer_to_receive to 1 or
   //    if it has been left as default.
   auto offer_direction =
-      cricket::RtpTransceiverDirection::FromMediaContentDirection(
+      cricket::RtpTransceiverDirectionFromMediaContentDirection(
           offer_direction_);
+  bool offer_send = RtpTransceiverDirectionHasSend(offer_direction);
+  bool offer_recv = RtpTransceiverDirectionHasRecv(offer_direction);
 
   // The negotiated components determine the direction set in the answer.
-  bool negotiate_send = (send_media_ && offer_direction.recv);
-  bool negotiate_recv = ((offer_to_receive_ != 0) && offer_direction.send);
+  bool negotiate_send = (send_media_ && offer_recv);
+  bool negotiate_recv = ((offer_to_receive_ != 0) && offer_send);
 
   auto expected_direction =
-      cricket::RtpTransceiverDirection(negotiate_send, negotiate_recv)
-          .ToMediaContentDirection();
+      cricket::MediaContentDirectionFromRtpTransceiverDirection(
+          RtpTransceiverDirectionFromSendRecv(negotiate_send, negotiate_recv));
   EXPECT_EQ(expected_direction,
             GetMediaContentDirection(answer.get(), cricket::CN_AUDIO));
 }
diff --git a/pc/rtpmediautils.cc b/pc/rtpmediautils.cc
new file mode 100644
index 0000000..ef86c0a
--- /dev/null
+++ b/pc/rtpmediautils.cc
@@ -0,0 +1,53 @@
+/*
+ *  Copyright 2017 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.
+ */
+
+#include "pc/rtpmediautils.h"
+
+namespace webrtc {
+
+RtpTransceiverDirection RtpTransceiverDirectionFromSendRecv(bool send,
+                                                            bool recv) {
+  if (send && recv) {
+    return RtpTransceiverDirection::kSendRecv;
+  } else if (send && !recv) {
+    return RtpTransceiverDirection::kSendOnly;
+  } else if (!send && recv) {
+    return RtpTransceiverDirection::kRecvOnly;
+  } else {
+    return RtpTransceiverDirection::kInactive;
+  }
+}
+
+bool RtpTransceiverDirectionHasSend(RtpTransceiverDirection direction) {
+  return direction == RtpTransceiverDirection::kSendRecv ||
+         direction == RtpTransceiverDirection::kSendOnly;
+}
+
+bool RtpTransceiverDirectionHasRecv(RtpTransceiverDirection direction) {
+  return direction == RtpTransceiverDirection::kSendRecv ||
+         direction == RtpTransceiverDirection::kRecvOnly;
+}
+
+RtpTransceiverDirection RtpTransceiverDirectionReversed(
+    RtpTransceiverDirection direction) {
+  switch (direction) {
+    case RtpTransceiverDirection::kSendRecv:
+    case RtpTransceiverDirection::kInactive:
+      return direction;
+    case RtpTransceiverDirection::kSendOnly:
+      return RtpTransceiverDirection::kRecvOnly;
+    case RtpTransceiverDirection::kRecvOnly:
+      return RtpTransceiverDirection::kSendOnly;
+  }
+  RTC_NOTREACHED();
+  return direction;
+}
+
+}  // namespace webrtc
diff --git a/pc/rtpmediautils.h b/pc/rtpmediautils.h
new file mode 100644
index 0000000..96426f4
--- /dev/null
+++ b/pc/rtpmediautils.h
@@ -0,0 +1,36 @@
+/*
+ *  Copyright 2017 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.
+ */
+
+#ifndef PC_RTPMEDIAUTILS_H_
+#define PC_RTPMEDIAUTILS_H_
+
+#include "api/rtptransceiverinterface.h"
+
+namespace webrtc {
+
+// Returns the RtpTransceiverDirection that satisfies specified send and receive
+// conditions.
+RtpTransceiverDirection RtpTransceiverDirectionFromSendRecv(bool send,
+                                                            bool recv);
+
+// Returns true only if the direction will send media.
+bool RtpTransceiverDirectionHasSend(RtpTransceiverDirection direction);
+
+// Returns true only if the direction will receive media.
+bool RtpTransceiverDirectionHasRecv(RtpTransceiverDirection direction);
+
+// Returns the RtpTransceiverDirection which is the reverse of the given
+// direction.
+RtpTransceiverDirection RtpTransceiverDirectionReversed(
+    RtpTransceiverDirection direction);
+
+}  // namespace webrtc
+
+#endif  // PC_RTPMEDIAUTILS_H_
diff --git a/pc/rtpmediautils_unittest.cc b/pc/rtpmediautils_unittest.cc
new file mode 100644
index 0000000..d72f35b
--- /dev/null
+++ b/pc/rtpmediautils_unittest.cc
@@ -0,0 +1,59 @@
+/*
+ *  Copyright 2017 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.
+ */
+
+#include "pc/rtpmediautils.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+using ::testing::Values;
+
+class EnumerateAllDirectionsTest
+    : public ::testing::Test,
+      public ::testing::WithParamInterface<RtpTransceiverDirection> {};
+
+// Test that converting the direction to send/recv and back again results in the
+// same direction.
+TEST_P(EnumerateAllDirectionsTest, TestIdentity) {
+  RtpTransceiverDirection direction = GetParam();
+
+  bool send = RtpTransceiverDirectionHasSend(direction);
+  bool recv = RtpTransceiverDirectionHasRecv(direction);
+
+  EXPECT_EQ(direction, RtpTransceiverDirectionFromSendRecv(send, recv));
+}
+
+// Test that reversing the direction is equivalent to swapping send/recv.
+TEST_P(EnumerateAllDirectionsTest, TestReversedSwapped) {
+  RtpTransceiverDirection direction = GetParam();
+
+  bool send = RtpTransceiverDirectionHasSend(direction);
+  bool recv = RtpTransceiverDirectionHasRecv(direction);
+
+  EXPECT_EQ(RtpTransceiverDirectionFromSendRecv(recv, send),
+            RtpTransceiverDirectionReversed(direction));
+}
+
+// Test that reversing the direction twice results in the same direction.
+TEST_P(EnumerateAllDirectionsTest, TestReversedIdentity) {
+  RtpTransceiverDirection direction = GetParam();
+
+  EXPECT_EQ(direction, RtpTransceiverDirectionReversed(
+                           RtpTransceiverDirectionReversed(direction)));
+}
+
+INSTANTIATE_TEST_CASE_P(RtpTransceiverDirectionTest,
+                        EnumerateAllDirectionsTest,
+                        Values(RtpTransceiverDirection::kSendRecv,
+                               RtpTransceiverDirection::kSendOnly,
+                               RtpTransceiverDirection::kRecvOnly,
+                               RtpTransceiverDirection::kInactive));
+
+}  // namespace webrtc