Implement PlayoutDelay extension as a trait
to be used with rtp::Packet class

BUG=webrtc:1994
R=isheriff@chromium.org, stefan@webrtc.org

Review URL: https://codereview.webrtc.org/2224063004 .

Cr-Commit-Position: refs/heads/master@{#14105}
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc b/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc
index 2d5dbf9..a851bc3 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc
@@ -195,4 +195,52 @@
   data[0] = value;
   return true;
 }
+
+//   0                   1                   2                   3
+//   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+//  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//  |  ID   | len=2 |   MIN delay           |   MAX delay           |
+//  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+constexpr RTPExtensionType PlayoutDelayLimits::kId;
+constexpr uint8_t PlayoutDelayLimits::kValueSizeBytes;
+const char* PlayoutDelayLimits::kName =
+    "http://www.webrtc.org/experiments/rtp-hdrext/playout-delay";
+bool PlayoutDelayLimits::IsSupportedFor(MediaType type) {
+  switch (type) {
+    case MediaType::ANY:
+    case MediaType::VIDEO:
+      return true;
+    case MediaType::AUDIO:
+    case MediaType::DATA:
+      return false;
+  }
+  RTC_NOTREACHED();
+  return false;
+}
+
+bool PlayoutDelayLimits::Parse(const uint8_t* data,
+                               PlayoutDelay* playout_delay) {
+  RTC_DCHECK(playout_delay);
+  uint32_t raw = ByteReader<uint32_t, 3>::ReadBigEndian(data);
+  uint16_t min_raw = (raw >> 12);
+  uint16_t max_raw = (raw & 0xfff);
+  if (min_raw > max_raw)
+    return false;
+  playout_delay->min_ms = min_raw * kGranularityMs;
+  playout_delay->max_ms = max_raw * kGranularityMs;
+  return true;
+}
+
+bool PlayoutDelayLimits::Write(uint8_t* data,
+                               const PlayoutDelay& playout_delay) {
+  RTC_DCHECK_LE(0, playout_delay.min_ms);
+  RTC_DCHECK_LE(playout_delay.min_ms, playout_delay.max_ms);
+  RTC_DCHECK_LE(playout_delay.max_ms, kMaxMs);
+  // Convert MS to value to be sent on extension header.
+  uint32_t min_delay = playout_delay.min_ms / kGranularityMs;
+  uint32_t max_delay = playout_delay.max_ms / kGranularityMs;
+  ByteWriter<uint32_t, 3>::WriteBigEndian(data, (min_delay << 12) | max_delay);
+  return true;
+}
+
 }  // namespace webrtc
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.h b/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.h
index 4800a60..95473f4 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.h
+++ b/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.h
@@ -75,5 +75,22 @@
   static bool Write(uint8_t* data, uint8_t value);
 };
 
+class PlayoutDelayLimits {
+ public:
+  static constexpr RTPExtensionType kId = kRtpExtensionPlayoutDelay;
+  static constexpr uint8_t kValueSizeBytes = 3;
+  static const char* kName;
+  static bool IsSupportedFor(MediaType type);
+  // Playout delay in milliseconds. A playout delay limit (min or max)
+  // has 12 bits allocated. This allows a range of 0-4095 values which
+  // translates to a range of 0-40950 in milliseconds.
+  static constexpr int kGranularityMs = 10;
+  // Maximum playout delay value in milliseconds.
+  static constexpr int kMaxMs = 0xfff * kGranularityMs;  // 40950.
+
+  static bool Parse(const uint8_t* data, PlayoutDelay* playout_delay);
+  static bool Write(uint8_t* data, const PlayoutDelay& playout_delay);
+};
+
 }  // namespace webrtc
 #endif  // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_HEADER_EXTENSIONS_H_
diff --git a/webrtc/test/fuzzers/rtp_packet_fuzzer.cc b/webrtc/test/fuzzers/rtp_packet_fuzzer.cc
index 2a67a8b..613f125 100644
--- a/webrtc/test/fuzzers/rtp_packet_fuzzer.cc
+++ b/webrtc/test/fuzzers/rtp_packet_fuzzer.cc
@@ -82,7 +82,8 @@
         packet.GetExtension<TransportSequenceNumber>(&seqnum);
         break;
       case kRtpExtensionPlayoutDelay:
-        // TODO(katrielc) Add this once it's written.
+        PlayoutDelay playout;
+        packet.GetExtension<PlayoutDelayLimits>(&playout);
         break;
     }
   }