Add CVO support to Vie layer.

1. standard plumbing CVO through vie layer.
2. added a rtp_cvo.h which has both conversion functions from rtp header byte to/from VideoRotation.

WebRTCVideoEngine will later pass the rotation info in SendFrame() through VieVideoFrameI420.

BUG=4145
R=mflodman@webrtc.org, tommi@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/46429007

Cr-Commit-Position: refs/heads/master@{#8703}
git-svn-id: http://webrtc.googlecode.com/svn/trunk@8703 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/media/base/constants.cc b/talk/media/base/constants.cc
index 46f29bf..6e1c460 100644
--- a/talk/media/base/constants.cc
+++ b/talk/media/base/constants.cc
@@ -109,6 +109,11 @@
 const char kRtpAbsoluteSenderTimeHeaderExtension[] =
     "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time";
 
+const int kRtpVideoRotationHeaderExtensionDefaultId = 4;
+const char kRtpVideoRotationHeaderExtension[] = "urn:3gpp:video-orientation";
+const char kRtpVideoRotation6BitsHeaderExtensionForTesting[] =
+    "urn:3gpp:video-orientation:6";
+
 const int kNumDefaultUnsignalledVideoRecvStreams = 0;
 
 
diff --git a/talk/media/base/constants.h b/talk/media/base/constants.h
index ce2748c..2fb4fdc 100644
--- a/talk/media/base/constants.h
+++ b/talk/media/base/constants.h
@@ -121,21 +121,29 @@
 
 extern const char kComfortNoiseCodecName[];
 
-// Extension header for audio levels, as defined in
+// Header extension for audio levels, as defined in
 // http://tools.ietf.org/html/draft-ietf-avtext-client-to-mixer-audio-level-03
 extern const int kRtpAudioLevelHeaderExtensionDefaultId;
 extern const char kRtpAudioLevelHeaderExtension[];
 
-// Extension header for RTP timestamp offset, see RFC 5450 for details:
+// Header extension for RTP timestamp offset, see RFC 5450 for details:
 // http://tools.ietf.org/html/rfc5450
 extern const int kRtpTimestampOffsetHeaderExtensionDefaultId;
 extern const char kRtpTimestampOffsetHeaderExtension[];
 
-// Extension header for absolute send time, see url for details:
+// Header extension for absolute send time, see url for details:
 // http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
 extern const int kRtpAbsoluteSenderTimeHeaderExtensionDefaultId;
 extern const char kRtpAbsoluteSenderTimeHeaderExtension[];
 
+// Header extension for coordination of video orientation, see url for details:
+// http://www.etsi.org/deliver/etsi_ts/126100_126199/126114/12.07.00_60/
+// ts_126114v120700p.pdf
+extern const int kRtpVideoRotationHeaderExtensionDefaultId;
+extern const char kRtpVideoRotationHeaderExtension[];
+// We don't support 6 bit CVO. Added here for testing purpose.
+extern const char kRtpVideoRotation6BitsHeaderExtensionForTesting[];
+
 extern const int kNumDefaultUnsignalledVideoRecvStreams;
 }  // namespace cricket
 
diff --git a/talk/media/webrtc/fakewebrtcvideoengine.h b/talk/media/webrtc/fakewebrtcvideoengine.h
index f64cd40..16c7f8b 100644
--- a/talk/media/webrtc/fakewebrtcvideoengine.h
+++ b/talk/media/webrtc/fakewebrtcvideoengine.h
@@ -285,6 +285,8 @@
           rtp_offset_receive_id_(-1),
           rtp_absolute_send_time_send_id_(-1),
           rtp_absolute_send_time_receive_id_(-1),
+          rtp_video_rotation_send_id_(-1),
+          rtp_video_rotation_receive_id_(-1),
           sender_target_delay_(0),
           receiver_target_delay_(0),
           transmission_smoothing_(false),
@@ -325,6 +327,8 @@
     int rtp_offset_receive_id_;
     int rtp_absolute_send_time_send_id_;
     int rtp_absolute_send_time_receive_id_;
+    int rtp_video_rotation_send_id_;
+    int rtp_video_rotation_receive_id_;
     int sender_target_delay_;
     int receiver_target_delay_;
     bool transmission_smoothing_;
@@ -503,6 +507,8 @@
       return channels_.find(channel)->second->rtp_offset_send_id_;
     } else if (extension == kRtpAbsoluteSenderTimeHeaderExtension) {
       return channels_.find(channel)->second->rtp_absolute_send_time_send_id_;
+    } else if (extension == kRtpVideoRotationHeaderExtension) {
+      return channels_.find(channel)->second->rtp_video_rotation_send_id_;
     }
     return -1;
   }
@@ -513,6 +519,8 @@
     } else if (extension == kRtpAbsoluteSenderTimeHeaderExtension) {
       return
           channels_.find(channel)->second->rtp_absolute_send_time_receive_id_;
+    } else if (extension == kRtpVideoRotationHeaderExtension) {
+      return channels_.find(channel)->second->rtp_video_rotation_receive_id_;
     }
     return -1;
   }
@@ -1107,25 +1115,36 @@
   WEBRTC_FUNC(SetSendTimestampOffsetStatus, (int channel, bool enable,
       int id)) {
     WEBRTC_CHECK_CHANNEL(channel);
-    channels_[channel]->rtp_offset_send_id_ = (enable) ? id : -1;
+    channels_[channel]->rtp_offset_send_id_ = enable? id : -1;
     return 0;
   }
   WEBRTC_FUNC(SetReceiveTimestampOffsetStatus, (int channel, bool enable,
       int id)) {
     WEBRTC_CHECK_CHANNEL(channel);
-    channels_[channel]->rtp_offset_receive_id_ = (enable) ? id : -1;
+    channels_[channel]->rtp_offset_receive_id_ = enable? id : -1;
     return 0;
   }
   WEBRTC_FUNC(SetSendAbsoluteSendTimeStatus, (int channel, bool enable,
       int id)) {
     WEBRTC_CHECK_CHANNEL(channel);
-    channels_[channel]->rtp_absolute_send_time_send_id_ = (enable) ? id : -1;
+    channels_[channel]->rtp_absolute_send_time_send_id_ = enable? id : -1;
     return 0;
   }
   WEBRTC_FUNC(SetReceiveAbsoluteSendTimeStatus, (int channel, bool enable,
       int id)) {
     WEBRTC_CHECK_CHANNEL(channel);
-    channels_[channel]->rtp_absolute_send_time_receive_id_ = (enable) ? id : -1;
+    channels_[channel]->rtp_absolute_send_time_receive_id_ = enable? id : -1;
+    return 0;
+  }
+  WEBRTC_FUNC(SetSendVideoRotationStatus, (int channel, bool enable, int id)) {
+    WEBRTC_CHECK_CHANNEL(channel);
+    channels_[channel]->rtp_video_rotation_send_id_ = enable? id : -1;
+    return 0;
+  }
+  WEBRTC_FUNC(SetReceiveVideoRotationStatus,
+              (int channel, bool enable, int id)) {
+    WEBRTC_CHECK_CHANNEL(channel);
+    channels_[channel]->rtp_video_rotation_receive_id_ = enable? id : -1;
     return 0;
   }
   WEBRTC_STUB(SetRtcpXrRrtrStatus, (int, bool));
diff --git a/webrtc/config.h b/webrtc/config.h
index 1777937..e43a588 100644
--- a/webrtc/config.h
+++ b/webrtc/config.h
@@ -51,6 +51,7 @@
 
   static const char* kTOffset;
   static const char* kAbsSendTime;
+  static const char* kVideoRotation;
   std::string name;
   int id;
 };
diff --git a/webrtc/modules/rtp_rtcp/interface/rtp_cvo.h b/webrtc/modules/rtp_rtcp/interface/rtp_cvo.h
new file mode 100644
index 0000000..c7a0268
--- /dev/null
+++ b/webrtc/modules/rtp_rtcp/interface/rtp_cvo.h
@@ -0,0 +1,54 @@
+/*
+ *  Copyright (c) 2015 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 WEBRTC_MODULES_RTP_RTCP_INTERFACE_RTP_CVO__H_
+#define WEBRTC_MODULES_RTP_RTCP_INTERFACE_RTP_CVO__H_
+
+#include "webrtc/common_video/rotation.h"
+
+namespace webrtc {
+
+// Please refer to http://www.etsi.org/deliver/etsi_ts/126100_126199/126114/
+// 12.07.00_60/ts_126114v120700p.pdf Section 7.4.5. The rotation of a frame is
+// the clockwise angle the frames must be rotated in order to display the frames
+// correctly if the display is rotated in its natural orientation.
+inline uint8_t ConvertVideoRotationToCVOByte(VideoRotation rotation) {
+  switch (rotation) {
+    case kVideoRotation_0:
+      return 0;
+    case kVideoRotation_90:
+      return 1;
+    case kVideoRotation_180:
+      return 2;
+    case kVideoRotation_270:
+      return 3;
+  }
+  assert(false);
+  return 0;
+}
+
+inline VideoRotation ConvertCVOByteToVideoRotation(uint8_t rotation) {
+  switch (rotation) {
+    case 0:
+      return kVideoRotation_0;
+    case 1:
+      return kVideoRotation_90;
+    case 2:
+      return kVideoRotation_180;
+      break;
+    case 3:
+      return kVideoRotation_270;
+    default:
+      assert(false);
+      return kVideoRotation_0;
+  }
+}
+
+}  // namespace webrtc
+#endif  // WEBRTC_MODULES_RTP_RTCP_INTERFACE_RTP_CVO__H_
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_receiver_video.cc b/webrtc/modules/rtp_rtcp/source/rtp_receiver_video.cc
index fd3b7af..2823868 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_receiver_video.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_receiver_video.cc
@@ -13,6 +13,7 @@
 #include <assert.h>
 #include <string.h>
 
+#include "webrtc/modules/rtp_rtcp/interface/rtp_cvo.h"
 #include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h"
 #include "webrtc/modules/rtp_rtcp/source/rtp_format.h"
 #include "webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h"
@@ -62,6 +63,13 @@
   const size_t payload_data_length =
       payload_length - rtp_header->header.paddingLength;
 
+  // Retrieve the video rotation information.
+  rtp_header->type.Video.rotation = kVideoRotation_0;
+  if (rtp_header->header.extension.hasVideoRotation) {
+    rtp_header->type.Video.rotation = ConvertCVOByteToVideoRotation(
+        rtp_header->header.extension.videoRotation);
+  }
+
   if (payload == NULL || payload_data_length == 0) {
     return data_callback_->OnReceivedPayloadData(NULL, 0, rtp_header) == 0 ? 0
                                                                            : -1;
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender.cc b/webrtc/modules/rtp_rtcp/source/rtp_sender.cc
index 28c458d..c01cb36 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_sender.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_sender.cc
@@ -12,6 +12,7 @@
 
 #include <stdlib.h>  // srand
 
+#include "webrtc/modules/rtp_rtcp/interface/rtp_cvo.h"
 #include "webrtc/modules/rtp_rtcp/source/rtp_sender_audio.h"
 #include "webrtc/modules/rtp_rtcp/source/rtp_sender_video.h"
 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
@@ -453,25 +454,6 @@
   return 0;
 }
 
-// Please refer to http://www.etsi.org/deliver/etsi_ts/126100_126199/126114/
-// 12.07.00_60/ts_126114v120700p.pdf Section 7.4.5. The rotation of a frame is
-// the clockwise angle the frames must be rotated in order to display the frames
-// correctly if the display is rotated in its natural orientation.
-uint8_t RTPSender::ConvertToCVOByte(VideoRotation rotation) {
-  switch (rotation) {
-    case kVideoRotation_0:
-      return 0;
-    case kVideoRotation_90:
-      return 1;
-    case kVideoRotation_180:
-      return 2;
-    case kVideoRotation_270:
-      return 3;
-  }
-  assert(false);
-  return 0;
-}
-
 int32_t RTPSender::SendOutgoingData(FrameType frame_type,
                                     int8_t payload_type,
                                     uint32_t capture_timestamp,
@@ -1367,7 +1349,7 @@
   size_t pos = 0;
   const uint8_t len = 0;
   data_buffer[pos++] = (id << 4) + len;
-  data_buffer[pos++] = ConvertToCVOByte(rotation_);
+  data_buffer[pos++] = ConvertVideoRotationToCVOByte(rotation_);
   data_buffer[pos++] = 0;  // padding
   data_buffer[pos++] = 0;  // padding
   assert(pos == kVideoRotationLength);
@@ -1508,7 +1490,7 @@
     LOG(LS_WARNING) << "Failed to update CVO.";
     return false;
   }
-  rtp_packet[block_pos + 1] = ConvertToCVOByte(rotation);
+  rtp_packet[block_pos + 1] = ConvertVideoRotationToCVOByte(rotation);
   return true;
 }
 
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender.h b/webrtc/modules/rtp_rtcp/source/rtp_sender.h
index 67fb1fe..72763ed 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_sender.h
+++ b/webrtc/modules/rtp_rtcp/source/rtp_sender.h
@@ -284,8 +284,6 @@
   void SetRtxRtpState(const RtpState& rtp_state);
   RtpState GetRtxRtpState() const;
 
-  static uint8_t ConvertToCVOByte(VideoRotation rotation);
-
  protected:
   int32_t CheckPayloadType(int8_t payload_type, RtpVideoCodecTypes* video_type);
 
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc
index c6e8398..e5c3b6f 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc
@@ -17,6 +17,7 @@
 #include "webrtc/base/buffer.h"
 #include "webrtc/base/scoped_ptr.h"
 #include "webrtc/modules/pacing/include/mock/mock_paced_sender.h"
+#include "webrtc/modules/rtp_rtcp/interface/rtp_cvo.h"
 #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
 #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
 #include "webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h"
@@ -189,7 +190,7 @@
     EXPECT_EQ(rtp_sender_->SSRC(), rtp_header.ssrc);
     EXPECT_EQ(0, rtp_header.numCSRCs);
     EXPECT_EQ(0U, rtp_header.paddingLength);
-    EXPECT_EQ(RTPSender::ConvertToCVOByte(rotation),
+    EXPECT_EQ(ConvertVideoRotationToCVOByte(rotation),
               rtp_header.extension.videoRotation);
   }
 };
@@ -427,7 +428,7 @@
   VerifyRTPHeaderCommon(rtp_header);
   EXPECT_EQ(length, rtp_header.headerLength);
   EXPECT_TRUE(rtp_header.extension.hasVideoRotation);
-  EXPECT_EQ(RTPSender::ConvertToCVOByte(kRotation),
+  EXPECT_EQ(ConvertVideoRotationToCVOByte(kRotation),
             rtp_header.extension.videoRotation);
 }
 
diff --git a/webrtc/video/call.cc b/webrtc/video/call.cc
index bf80ee5..21a78da 100644
--- a/webrtc/video/call.cc
+++ b/webrtc/video/call.cc
@@ -41,10 +41,12 @@
 const char* RtpExtension::kTOffset = "urn:ietf:params:rtp-hdrext:toffset";
 const char* RtpExtension::kAbsSendTime =
     "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time";
+const char* RtpExtension::kVideoRotation = "urn:3gpp:video-orientation";
 
 bool RtpExtension::IsSupported(const std::string& name) {
   return name == webrtc::RtpExtension::kTOffset ||
-         name == webrtc::RtpExtension::kAbsSendTime;
+         name == webrtc::RtpExtension::kAbsSendTime ||
+         name == webrtc::RtpExtension::kVideoRotation;
 }
 
 VideoEncoder* VideoEncoder::Create(VideoEncoder::EncoderType codec_type) {
diff --git a/webrtc/video/video_receive_stream.cc b/webrtc/video/video_receive_stream.cc
index 8665c11..ce65bcd 100644
--- a/webrtc/video/video_receive_stream.cc
+++ b/webrtc/video/video_receive_stream.cc
@@ -181,6 +181,9 @@
     } else if (extension == RtpExtension::kAbsSendTime) {
       if (rtp_rtcp_->SetReceiveAbsoluteSendTimeStatus(channel_, true, id) != 0)
         abort();
+    } else if (extension == RtpExtension::kVideoRotation) {
+      if (rtp_rtcp_->SetReceiveVideoRotationStatus(channel_, true, id) != 0)
+        abort();
     } else {
       abort();  // Unsupported extension.
     }
diff --git a/webrtc/video/video_send_stream.cc b/webrtc/video/video_send_stream.cc
index 881802a..65e45e6 100644
--- a/webrtc/video/video_send_stream.cc
+++ b/webrtc/video/video_send_stream.cc
@@ -144,6 +144,9 @@
     } else if (extension == RtpExtension::kAbsSendTime) {
       if (rtp_rtcp_->SetSendAbsoluteSendTimeStatus(channel_, true, id) != 0)
         abort();
+    } else if (extension == RtpExtension::kVideoRotation) {
+      if (rtp_rtcp_->SetSendVideoRotationStatus(channel_, true, id) != 0)
+        abort();
     } else {
       abort();  // Unsupported extension.
     }
diff --git a/webrtc/video_engine/include/vie_capture.h b/webrtc/video_engine/include/vie_capture.h
index caaeace..35c8469 100644
--- a/webrtc/video_engine/include/vie_capture.h
+++ b/webrtc/video_engine/include/vie_capture.h
@@ -71,6 +71,7 @@
     v_pitch = 0;
     width = 0;
     height = 0;
+    rotation = kVideoRotation_0;
   }
 
   unsigned char* y_plane;
@@ -83,6 +84,7 @@
 
   unsigned short width;
   unsigned short height;
+  VideoRotation rotation;
 };
 
 // This class declares an abstract interface to be used when implementing
diff --git a/webrtc/video_engine/include/vie_rtp_rtcp.h b/webrtc/video_engine/include/vie_rtp_rtcp.h
index 7446e5f..d9a6a1f 100644
--- a/webrtc/video_engine/include/vie_rtp_rtcp.h
+++ b/webrtc/video_engine/include/vie_rtp_rtcp.h
@@ -245,6 +245,14 @@
                                                bool enable,
                                                int id) = 0;
 
+  virtual int SetSendVideoRotationStatus(int video_channel,
+                                         bool enable,
+                                         int id) = 0;
+
+  virtual int SetReceiveVideoRotationStatus(int video_channel,
+                                            bool enable,
+                                            int id) = 0;
+
   // Enables/disables RTCP Receiver Reference Time Report Block extension/
   // DLRR Report Block extension (RFC 3611).
   virtual int SetRtcpXrRrtrStatus(int video_channel, bool enable) = 0;
diff --git a/webrtc/video_engine/vie_capturer.cc b/webrtc/video_engine/vie_capturer.cc
index bc96425..b35be3e 100644
--- a/webrtc/video_engine/vie_capturer.cc
+++ b/webrtc/video_engine/vie_capturer.cc
@@ -325,7 +325,8 @@
                                        video_frame.height,
                                        video_frame.y_pitch,
                                        video_frame.u_pitch,
-                                       video_frame.v_pitch);
+                                       video_frame.v_pitch,
+                                       video_frame.rotation);
 
   if (ret < 0) {
     LOG_F(LS_ERROR) << "Could not create I420Frame.";
diff --git a/webrtc/video_engine/vie_channel.cc b/webrtc/video_engine/vie_channel.cc
index eb16b1f..1101213 100644
--- a/webrtc/video_engine/vie_channel.cc
+++ b/webrtc/video_engine/vie_channel.cc
@@ -118,6 +118,7 @@
       bandwidth_observer_(bandwidth_observer),
       send_timestamp_extension_id_(kInvalidRtpExtensionId),
       absolute_send_time_extension_id_(kInvalidRtpExtensionId),
+      video_rotation_extension_id_(kInvalidRtpExtensionId),
       external_transport_(NULL),
       decoder_reset_(true),
       wait_for_key_frame_(false),
@@ -459,6 +460,7 @@
         if (rtp_rtcp->RegisterSendRtpHeaderExtension(
             kRtpExtensionTransmissionTimeOffset,
             send_timestamp_extension_id_) != 0) {
+          LOG(LS_WARNING) << "Register Transmission Time Offset failed";
         }
       } else {
         rtp_rtcp->DeregisterSendRtpHeaderExtension(
@@ -471,11 +473,23 @@
         if (rtp_rtcp->RegisterSendRtpHeaderExtension(
             kRtpExtensionAbsoluteSendTime,
             absolute_send_time_extension_id_) != 0) {
+          LOG(LS_WARNING) << "Register Absolute Send Time failed";
         }
       } else {
         rtp_rtcp->DeregisterSendRtpHeaderExtension(
             kRtpExtensionAbsoluteSendTime);
       }
+      if (video_rotation_extension_id_ != kInvalidRtpExtensionId) {
+        // Deregister in case the extension was previously enabled.
+        rtp_rtcp->DeregisterSendRtpHeaderExtension(kRtpExtensionVideoRotation);
+        if (rtp_rtcp->RegisterSendRtpHeaderExtension(
+                kRtpExtensionVideoRotation, video_rotation_extension_id_) !=
+            0) {
+          LOG(LS_WARNING) << "Register VideoRotation extension failed";
+        }
+      } else {
+        rtp_rtcp->DeregisterSendRtpHeaderExtension(kRtpExtensionVideoRotation);
+      }
       rtp_rtcp->RegisterRtcpStatisticsCallback(
           rtp_rtcp_->GetRtcpStatisticsCallback());
       rtp_rtcp->RegisterSendChannelRtpStatisticsCallback(
@@ -899,6 +913,37 @@
   return vie_receiver_.SetReceiveAbsoluteSendTimeStatus(enable, id) ? 0 : -1;
 }
 
+int ViEChannel::SetSendVideoRotationStatus(bool enable, int id) {
+  CriticalSectionScoped cs(rtp_rtcp_cs_.get());
+  int error = 0;
+  if (enable) {
+    // Enable the extension, but disable possible old id to avoid errors.
+    video_rotation_extension_id_ = id;
+    rtp_rtcp_->DeregisterSendRtpHeaderExtension(kRtpExtensionVideoRotation);
+    error = rtp_rtcp_->RegisterSendRtpHeaderExtension(
+        kRtpExtensionVideoRotation, id);
+    for (std::list<RtpRtcp*>::iterator it = simulcast_rtp_rtcp_.begin();
+         it != simulcast_rtp_rtcp_.end(); it++) {
+      (*it)->DeregisterSendRtpHeaderExtension(kRtpExtensionVideoRotation);
+      error |=
+          (*it)->RegisterSendRtpHeaderExtension(kRtpExtensionVideoRotation, id);
+    }
+  } else {
+    // Disable the extension.
+    video_rotation_extension_id_ = kInvalidRtpExtensionId;
+    rtp_rtcp_->DeregisterSendRtpHeaderExtension(kRtpExtensionVideoRotation);
+    for (std::list<RtpRtcp*>::iterator it = simulcast_rtp_rtcp_.begin();
+         it != simulcast_rtp_rtcp_.end(); it++) {
+      (*it)->DeregisterSendRtpHeaderExtension(kRtpExtensionVideoRotation);
+    }
+  }
+  return error;
+}
+
+int ViEChannel::SetReceiveVideoRotationStatus(bool enable, int id) {
+  return vie_receiver_.SetReceiveVideoRotationStatus(enable, id) ? 0 : -1;
+}
+
 void ViEChannel::SetRtcpXrRrtrStatus(bool enable) {
   CriticalSectionScoped cs(rtp_rtcp_cs_.get());
   rtp_rtcp_->SetRtcpXrRrtrStatus(enable);
diff --git a/webrtc/video_engine/vie_channel.h b/webrtc/video_engine/vie_channel.h
index 3744d88..1bf95f8 100644
--- a/webrtc/video_engine/vie_channel.h
+++ b/webrtc/video_engine/vie_channel.h
@@ -131,6 +131,8 @@
   int SetSendAbsoluteSendTimeStatus(bool enable, int id);
   int SetReceiveAbsoluteSendTimeStatus(bool enable, int id);
   bool GetReceiveAbsoluteSendTimeStatus() const;
+  int SetSendVideoRotationStatus(bool enable, int id);
+  int SetReceiveVideoRotationStatus(bool enable, int id);
   void SetRtcpXrRrtrStatus(bool enable);
   void SetTransmissionSmoothingStatus(bool enable);
   void EnableTMMBR(bool enable);
@@ -526,6 +528,7 @@
   rtc::scoped_ptr<RtcpBandwidthObserver> bandwidth_observer_;
   int send_timestamp_extension_id_;
   int absolute_send_time_extension_id_;
+  int video_rotation_extension_id_;
 
   Transport* external_transport_;
 
diff --git a/webrtc/video_engine/vie_receiver.cc b/webrtc/video_engine/vie_receiver.cc
index 0d1f1ad..e61c82b 100644
--- a/webrtc/video_engine/vie_receiver.cc
+++ b/webrtc/video_engine/vie_receiver.cc
@@ -16,6 +16,7 @@
 #include "webrtc/modules/rtp_rtcp/interface/fec_receiver.h"
 #include "webrtc/modules/rtp_rtcp/interface/receive_statistics.h"
 #include "webrtc/modules/rtp_rtcp/interface/remote_ntp_time_estimator.h"
+#include "webrtc/modules/rtp_rtcp/interface/rtp_cvo.h"
 #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
 #include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h"
 #include "webrtc/modules/rtp_rtcp/interface/rtp_receiver.h"
@@ -58,6 +59,7 @@
       receiving_(false),
       restored_packet_in_use_(false),
       receiving_ast_enabled_(false),
+      receiving_cvo_enabled_(false),
       last_packet_log_ms_(-1) {
   assert(remote_bitrate_estimator);
 }
@@ -187,6 +189,22 @@
   }
 }
 
+bool ViEReceiver::SetReceiveVideoRotationStatus(bool enable, int id) {
+  if (enable) {
+    if (rtp_header_parser_->RegisterRtpHeaderExtension(
+            kRtpExtensionVideoRotation, id)) {
+      receiving_cvo_enabled_ = true;
+      return true;
+    } else {
+      return false;
+    }
+  } else {
+    receiving_cvo_enabled_ = false;
+    return rtp_header_parser_->DeregisterRtpHeaderExtension(
+        kRtpExtensionVideoRotation);
+  }
+}
+
 int ViEReceiver::ReceivedRTPPacket(const void* rtp_packet,
                                    size_t rtp_packet_length,
                                    const PacketTime& packet_time) {
@@ -382,6 +400,11 @@
     return;
   }
   rtp_header.type.Video.codec = payload_specific.Video.videoCodecType;
+  rtp_header.type.Video.rotation = kVideoRotation_0;
+  if (header.extension.hasVideoRotation) {
+    rtp_header.type.Video.rotation =
+        ConvertCVOByteToVideoRotation(header.extension.videoRotation);
+  }
   OnReceivedPayloadData(NULL, 0, &rtp_header);
 }
 
diff --git a/webrtc/video_engine/vie_receiver.h b/webrtc/video_engine/vie_receiver.h
index e2e3daf..5c09a3e 100644
--- a/webrtc/video_engine/vie_receiver.h
+++ b/webrtc/video_engine/vie_receiver.h
@@ -64,6 +64,7 @@
 
   bool SetReceiveTimestampOffsetStatus(bool enable, int id);
   bool SetReceiveAbsoluteSendTimeStatus(bool enable, int id);
+  bool SetReceiveVideoRotationStatus(bool enable, int id);
 
   void StartReceive();
   void StopReceive();
@@ -123,6 +124,7 @@
   uint8_t restored_packet_[kViEMaxMtu];
   bool restored_packet_in_use_;
   bool receiving_ast_enabled_;
+  bool receiving_cvo_enabled_;
   int64_t last_packet_log_ms_;
 };
 
diff --git a/webrtc/video_engine/vie_rtp_rtcp_impl.cc b/webrtc/video_engine/vie_rtp_rtcp_impl.cc
index 58e750d..38e7e97 100644
--- a/webrtc/video_engine/vie_rtp_rtcp_impl.cc
+++ b/webrtc/video_engine/vie_rtp_rtcp_impl.cc
@@ -615,6 +615,43 @@
   return 0;
 }
 
+int ViERTP_RTCPImpl::SetSendVideoRotationStatus(int video_channel,
+                                                bool enable,
+                                                int id) {
+  LOG_F(LS_INFO) << "channel: " << video_channel
+                 << " enable: " << (enable ? "on" : "off") << " id: " << id;
+
+  ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
+  ViEChannel* vie_channel = cs.Channel(video_channel);
+  if (!vie_channel) {
+    shared_data_->SetLastError(kViERtpRtcpInvalidChannelId);
+    return -1;
+  }
+  if (vie_channel->SetSendVideoRotationStatus(enable, id) != 0) {
+    shared_data_->SetLastError(kViERtpRtcpUnknownError);
+    return -1;
+  }
+  return 0;
+}
+
+int ViERTP_RTCPImpl::SetReceiveVideoRotationStatus(int video_channel,
+                                                   bool enable,
+                                                   int id) {
+  LOG_F(LS_INFO) << "channel: " << video_channel
+                 << " enable: " << (enable ? "on" : "off") << " id: " << id;
+  ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
+  ViEChannel* vie_channel = cs.Channel(video_channel);
+  if (!vie_channel) {
+    shared_data_->SetLastError(kViERtpRtcpInvalidChannelId);
+    return -1;
+  }
+  if (vie_channel->SetReceiveVideoRotationStatus(enable, id) != 0) {
+    shared_data_->SetLastError(kViERtpRtcpUnknownError);
+    return -1;
+  }
+  return 0;
+}
+
 int ViERTP_RTCPImpl::SetRtcpXrRrtrStatus(int video_channel, bool enable) {
   LOG_F(LS_INFO) << "channel: " << video_channel
                  << " enable: " << (enable ? "on" : "off");
diff --git a/webrtc/video_engine/vie_rtp_rtcp_impl.h b/webrtc/video_engine/vie_rtp_rtcp_impl.h
index 7512222..b6f3a23 100644
--- a/webrtc/video_engine/vie_rtp_rtcp_impl.h
+++ b/webrtc/video_engine/vie_rtp_rtcp_impl.h
@@ -90,6 +90,12 @@
   virtual int SetReceiveAbsoluteSendTimeStatus(int video_channel,
                                                bool enable,
                                                int id);
+  virtual int SetSendVideoRotationStatus(int video_channel,
+                                         bool enable,
+                                         int id);
+  virtual int SetReceiveVideoRotationStatus(int video_channel,
+                                            bool enable,
+                                            int id);
   virtual int SetRtcpXrRrtrStatus(int video_channel, bool enable);
   virtual int SetTransmissionSmoothingStatus(int video_channel, bool enable);
   virtual int SetMinTransmitBitrate(int video_channel,