summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build/common.gypi1
-rw-r--r--build/config/features.gni1
-rw-r--r--extensions/extensions.gypi3
-rw-r--r--extensions/extensions_tests.gyp5
-rw-r--r--extensions/extensions_tests.gypi3
-rw-r--r--extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_packetizer.cc188
-rw-r--r--extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_packetizer.h72
-rw-r--r--extensions/renderer/api/display_source/wifi_display/wifi_display_media_packetizer_unittest.cc166
-rw-r--r--extensions/renderer/api/display_source/wifi_display/wifi_display_stream_packet_part.h43
9 files changed, 482 insertions, 0 deletions
diff --git a/build/common.gypi b/build/common.gypi
index ac255aa..3289014 100644
--- a/build/common.gypi
+++ b/build/common.gypi
@@ -89,6 +89,7 @@
'enable_wayland_server%' : 0,
# Enable Wi-Fi Display support.
+ # WARNING: This enables MPEG Transport Stream (MPEG-TS) encoding!
'enable_wifi_display%' : 0,
# By default we build against a stable sysroot image to avoid
diff --git a/build/config/features.gni b/build/config/features.gni
index c965844..27906ec 100644
--- a/build/config/features.gni
+++ b/build/config/features.gni
@@ -99,6 +99,7 @@ declare_args() {
use_cups = (is_desktop_linux || is_mac) && !is_chromecast && !is_headless
# Enables Wi-Fi Display functionality
+ # WARNING: This enables MPEG Transport Stream (MPEG-TS) encoding!
enable_wifi_display = false
}
diff --git a/extensions/extensions.gypi b/extensions/extensions.gypi
index 84777bd..b9c9ec0 100644
--- a/extensions/extensions.gypi
+++ b/extensions/extensions.gypi
@@ -1034,10 +1034,13 @@
'renderer/worker_script_context_set.h',
],
'extensions_renderer_sources_wifi_display': [
+ 'renderer/api/display_source/wifi_display/wifi_display_elementary_stream_packetizer.cc',
+ 'renderer/api/display_source/wifi_display/wifi_display_elementary_stream_packetizer.h',
'renderer/api/display_source/wifi_display/wifi_display_media_manager.cc',
'renderer/api/display_source/wifi_display/wifi_display_media_manager.h',
'renderer/api/display_source/wifi_display/wifi_display_session.cc',
'renderer/api/display_source/wifi_display/wifi_display_session.h',
+ 'renderer/api/display_source/wifi_display/wifi_display_stream_packet_part.h',
],
'extensions_utility_sources': [
'utility/unpacker.cc',
diff --git a/extensions/extensions_tests.gyp b/extensions/extensions_tests.gyp
index 11a27ca..bd131e0 100644
--- a/extensions/extensions_tests.gyp
+++ b/extensions/extensions_tests.gyp
@@ -61,6 +61,11 @@
'browser/api/webcam_private/visca_webcam_unittest.cc',
],
}],
+ ['enable_wifi_display==1', {
+ 'sources': [
+ '<@(extensions_unittests_sources_wifi_display)',
+ ],
+ }],
],
},
{
diff --git a/extensions/extensions_tests.gypi b/extensions/extensions_tests.gypi
index b226110..b14063c 100644
--- a/extensions/extensions_tests.gypi
+++ b/extensions/extensions_tests.gypi
@@ -170,5 +170,8 @@
'test/extensions_unittests_main.cc',
'utility/unpacker_unittest.cc',
],
+ 'extensions_unittests_sources_wifi_display': [
+ 'renderer/api/display_source/wifi_display/wifi_display_media_packetizer_unittest.cc',
+ ],
},
}
diff --git a/extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_packetizer.cc b/extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_packetizer.cc
new file mode 100644
index 0000000..39a6e3f
--- /dev/null
+++ b/extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_packetizer.cc
@@ -0,0 +1,188 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_packetizer.h"
+
+#include <cstring>
+
+#include "base/logging.h"
+
+namespace extensions {
+namespace {
+
+// Code and parameters related to the Packetized Elementary Stream (PES)
+// specification.
+namespace pes {
+
+const size_t kOptionalHeaderBaseSize = 3u;
+const size_t kPacketHeaderBaseSize = 6u;
+const size_t kTimeStampSize = 5u;
+const size_t kPacketHeaderMaxSize =
+ kPacketHeaderBaseSize + kOptionalHeaderBaseSize + 2u * kTimeStampSize;
+
+const size_t kUnitDataAlignment = 4u;
+
+size_t FillInTimeStamp(uint8_t* dst,
+ uint8_t pts_dts_indicator,
+ const base::TimeTicks& ts) {
+ // Convert to the number of 90 kHz ticks since some epoch.
+ // Always round up so that the number of ticks is never smaller than
+ // the number of program clock reference base ticks (which is not rounded
+ // because program clock reference is encoded with higher precision).
+ const uint64_t us =
+ static_cast<uint64_t>((ts - base::TimeTicks()).InMicroseconds());
+ const uint64_t n = (us * 90u + 999u) / 1000u;
+
+ // Expand PTS DTS indicator and a 33 bit time stamp to 40 bits:
+ // * 4 PTS DTS indicator bits, 3 time stamp bits, 1 on bit
+ // * 15 time stamp bits, 1 on bit
+ // * 15 time stamp bits, 1 on bit
+ size_t i = 0u;
+ dst[i++] = (pts_dts_indicator << 4) | (((n >> 30) & 0x7u) << 1) | (0x1u << 0);
+ dst[i++] = (n >> 22) & 0xFFu;
+ dst[i++] = (((n >> 15) & 0x7Fu) << 1) | (0x1u << 0);
+ dst[i++] = (n >> 7) & 0xFFu;
+ dst[i++] = (((n >> 0) & 0x7Fu) << 1) | (0x1u << 0);
+ DCHECK_EQ(i, kTimeStampSize);
+ return i;
+}
+
+size_t FillInOptionalHeader(uint8_t* dst,
+ const base::TimeTicks& pts,
+ const base::TimeTicks& dts,
+ size_t unit_header_size) {
+ size_t i = 0u;
+ dst[i++] = (0x2u << 6) | // Marker bits (0b10)
+ (0x0u << 4) | // Scrambling control (0b00 for not)
+ (0x0u << 3) | // Priority
+ (0x0u << 2) | // Data alignment indicator (0b0 for not)
+ (0x0u << 1) | // Copyright (0b0 for not)
+ (0x0u << 0); // Original (0b0 for copy)
+ const uint8_t pts_dts_indicator =
+ !pts.is_null() ? (!dts.is_null() ? 0x3u : 0x2u) : 0x0u;
+ dst[i++] = (pts_dts_indicator << 6) | // PTS DTS indicator
+ (0x0u << 5) | // ESCR flag
+ (0x0u << 4) | // ES rate flag
+ (0x0u << 3) | // DSM trick mode flag
+ (0x0u << 2) | // Additional copy info flag
+ (0x0u << 1) | // CRC flag
+ (0x0u << 0); // Extension flag
+ const size_t header_length_index = i++;
+ const size_t optional_header_base_end_index = i;
+ DCHECK_EQ(i, kOptionalHeaderBaseSize);
+
+ // Optional fields:
+ // PTS and DTS.
+ if (!pts.is_null()) {
+ i += FillInTimeStamp(&dst[i], pts_dts_indicator, pts);
+ if (!dts.is_null())
+ i += FillInTimeStamp(&dst[i], 0x1u, dts);
+ }
+
+ // Stuffing bytes (for unit data alignment).
+ const size_t remainder =
+ (kPacketHeaderBaseSize + i + unit_header_size) % kUnitDataAlignment;
+ if (remainder) {
+ const size_t n = kUnitDataAlignment - remainder;
+ std::memset(&dst[i], 0xFF, n);
+ i += n;
+ }
+
+ dst[header_length_index] = i - optional_header_base_end_index;
+ return i;
+}
+
+size_t FillInPacketHeader(uint8_t* dst,
+ uint8_t stream_id,
+ const base::TimeTicks& pts,
+ const base::TimeTicks& dts,
+ size_t unit_header_size,
+ size_t unit_size) {
+ // Reserve space for packet header base.
+ size_t i = kPacketHeaderBaseSize;
+ const size_t header_base_end_index = i;
+
+ // Fill in optional header.
+ i += FillInOptionalHeader(&dst[i], pts, dts, unit_header_size);
+
+ // Compute packet length.
+ size_t packet_length =
+ (i - header_base_end_index) + unit_header_size + unit_size;
+ if (packet_length >> 16) {
+ // The packet length is too large to be represented. That should only
+ // happen for video frames for which the packet length is not mandatory
+ // but may be set to 0, too.
+ DCHECK_GE(static_cast<unsigned>(stream_id),
+ WiFiDisplayElementaryStreamPacketizer::kFirstVideoStreamId);
+ DCHECK_LE(static_cast<unsigned>(stream_id),
+ WiFiDisplayElementaryStreamPacketizer::kLastVideoStreamId);
+ packet_length = 0u;
+ }
+
+ // Fill in packet header base.
+ size_t j = 0u;
+ dst[j++] = 0x00u; // Packet start code prefix (0x000001 in three bytes).
+ dst[j++] = 0x00u; //
+ dst[j++] = 0x01u; //
+ dst[j++] = stream_id;
+ dst[j++] = packet_length >> 8;
+ dst[j++] = packet_length & 0xFFu;
+ DCHECK_EQ(j, kPacketHeaderBaseSize);
+
+ return i;
+}
+
+} // namespace pes
+
+} // namespace
+
+WiFiDisplayElementaryStreamPacket::WiFiDisplayElementaryStreamPacket(
+ const HeaderBuffer& header_data,
+ size_t header_size,
+ const uint8_t* unit_header_data,
+ size_t unit_header_size,
+ const uint8_t* unit_data,
+ size_t unit_size)
+ : header_(header_buffer_, header_size),
+ unit_header_(unit_header_data, unit_header_size),
+ unit_(unit_data, unit_size) {
+ // Copy the actual header data bytes from the |header_data| argument to
+ // the |header_buffer_| member buffer used in the member initialization list.
+ std::memcpy(header_buffer_, header_data, header_.size());
+}
+
+WiFiDisplayElementaryStreamPacket::WiFiDisplayElementaryStreamPacket(
+ WiFiDisplayElementaryStreamPacket&& other)
+ : header_(header_buffer_, other.header().size()),
+ unit_header_(other.unit_header().data(), other.unit_header().size()),
+ unit_(other.unit().data(), other.unit().size()) {
+ // Copy the actual header data bytes from |other.header().data()| to
+ // the |header_buffer_| member buffer used in the member initialization list.
+ std::memcpy(header_buffer_, other.header().data(), header_.size());
+}
+
+// static
+WiFiDisplayElementaryStreamPacket
+WiFiDisplayElementaryStreamPacketizer::EncodeElementaryStreamUnit(
+ uint8_t stream_id,
+ const uint8_t* unit_header_data,
+ size_t unit_header_size,
+ const uint8_t* unit_data,
+ size_t unit_size,
+ const base::TimeTicks& pts,
+ const base::TimeTicks& dts) {
+ if (!unit_header_data) {
+ DCHECK_EQ(0u, unit_header_size);
+ unit_header_data = unit_data;
+ }
+
+ uint8_t header_data[pes::kPacketHeaderMaxSize];
+ size_t header_size = pes::FillInPacketHeader(header_data, stream_id, pts, dts,
+ unit_header_size, unit_size);
+ return WiFiDisplayElementaryStreamPacket(header_data, header_size,
+ unit_header_data, unit_header_size,
+ unit_data, unit_size);
+}
+
+} // namespace extensions
diff --git a/extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_packetizer.h b/extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_packetizer.h
new file mode 100644
index 0000000..4201a58
--- /dev/null
+++ b/extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_packetizer.h
@@ -0,0 +1,72 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_RENDERER_API_DISPLAY_SOURCE_WIFI_DISPLAY_WIFI_DISPLAY_ELEMENTARY_STREAM_PACKETIZER_H_
+#define EXTENSIONS_RENDERER_API_DISPLAY_SOURCE_WIFI_DISPLAY_WIFI_DISPLAY_ELEMENTARY_STREAM_PACKETIZER_H_
+
+#include "base/time/time.h"
+#include "extensions/renderer/api/display_source/wifi_display/wifi_display_stream_packet_part.h"
+
+namespace extensions {
+
+// WiFi Display elementary stream packet represents a Packetized Elementary
+// Stream (PES) packet containing WiFi Display elementary stream unit data.
+class WiFiDisplayElementaryStreamPacket {
+ public:
+ using HeaderBuffer = uint8_t[19];
+
+ WiFiDisplayElementaryStreamPacket(const HeaderBuffer& header_data,
+ size_t header_size,
+ const uint8_t* unit_header_data,
+ size_t unit_header_size,
+ const uint8_t* unit_data,
+ size_t unit_size);
+ // WiFiDisplayElementaryStreamPacketizer::EncodeElementaryStreamUnit returns
+ // WiFiDisplayElementaryStreamPacket so WiFiDisplayElementaryStreamPacket
+ // must be move constructible (as it is not copy constructible).
+ // A compiler should however use return value optimization and elide each
+ // call to this move constructor.
+ WiFiDisplayElementaryStreamPacket(WiFiDisplayElementaryStreamPacket&& other);
+
+ const WiFiDisplayStreamPacketPart& header() const { return header_; }
+ const WiFiDisplayStreamPacketPart& unit_header() const {
+ return unit_header_;
+ }
+ const WiFiDisplayStreamPacketPart& unit() const { return unit_; }
+
+ private:
+ HeaderBuffer header_buffer_;
+ WiFiDisplayStreamPacketPart header_;
+ WiFiDisplayStreamPacketPart unit_header_;
+ WiFiDisplayStreamPacketPart unit_;
+
+ DISALLOW_COPY_AND_ASSIGN(WiFiDisplayElementaryStreamPacket);
+};
+
+// The WiFi Display elementary stream packetizer packetizes unit buffers to
+// Packetized Elementary Stream (PES) packets.
+// It is used internally by a WiFi Display transport stream packetizer.
+class WiFiDisplayElementaryStreamPacketizer {
+ public:
+ enum : uint8_t {
+ kPrivateStream1Id = 0xBDu,
+ kFirstAudioStreamId = 0xC0u,
+ kLastAudioStreamId = 0xDFu,
+ kFirstVideoStreamId = 0xE0u,
+ kLastVideoStreamId = 0xEFu,
+ };
+
+ static WiFiDisplayElementaryStreamPacket EncodeElementaryStreamUnit(
+ uint8_t stream_id,
+ const uint8_t* unit_header_data,
+ size_t unit_header_size,
+ const uint8_t* unit_data,
+ size_t unit_size,
+ const base::TimeTicks& pts,
+ const base::TimeTicks& dts);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_RENDERER_API_DISPLAY_SOURCE_WIFI_DISPLAY_WIFI_DISPLAY_ELEMENTARY_STREAM_PACKETIZER_H_
diff --git a/extensions/renderer/api/display_source/wifi_display/wifi_display_media_packetizer_unittest.cc b/extensions/renderer/api/display_source/wifi_display/wifi_display_media_packetizer_unittest.cc
new file mode 100644
index 0000000..a8b769a
--- /dev/null
+++ b/extensions/renderer/api/display_source/wifi_display/wifi_display_media_packetizer_unittest.cc
@@ -0,0 +1,166 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/big_endian.h"
+#include "extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_packetizer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+
+namespace {
+
+namespace pes {
+const unsigned kDtsFlag = 0x0040u;
+const unsigned kMarkerFlag = 0x8000u;
+const unsigned kPtsFlag = 0x0080u;
+const size_t kUnitDataAlignment = sizeof(uint32_t);
+}
+
+uint64_t ParseTimeStamp(const uint8_t ts_bytes[5], uint8_t pts_dts_indicator) {
+ EXPECT_EQ(pts_dts_indicator, (ts_bytes[0] & 0xF0u) >> 4);
+ EXPECT_EQ(0x01u, ts_bytes[0] & 0x01u);
+ EXPECT_EQ(0x01u, ts_bytes[2] & 0x01u);
+ EXPECT_EQ(0x01u, ts_bytes[4] & 0x01u);
+ uint64_t ts = 0u;
+ ts = (ts_bytes[0] & 0x0Eu) >> 1;
+ ts = (ts << 8) | ts_bytes[1];
+ ts = (ts << 7) | ((ts_bytes[2] & 0xFEu) >> 1);
+ ts = (ts << 8) | ts_bytes[3];
+ ts = (ts << 7) | ((ts_bytes[4] & 0xFEu) >> 1);
+ return ts;
+}
+
+class WiFiDisplayElementaryStreamUnitPacketizationTest
+ : public testing::TestWithParam<
+ testing::tuple<unsigned, base::TimeDelta, base::TimeDelta>> {
+ protected:
+ static base::TimeTicks SumOrNull(const base::TimeTicks& base,
+ const base::TimeDelta& delta) {
+ return delta.is_max() ? base::TimeTicks() : base + delta;
+ }
+
+ WiFiDisplayElementaryStreamUnitPacketizationTest()
+ : unit_(testing::get<0>(GetParam())),
+ now_(base::TimeTicks::Now()),
+ dts_(SumOrNull(now_, testing::get<1>(GetParam()))),
+ pts_(SumOrNull(now_, testing::get<2>(GetParam()))) {}
+
+ void CheckElementaryStreamPacketHeader(
+ const WiFiDisplayElementaryStreamPacket& packet,
+ uint8_t stream_id) {
+ base::BigEndianReader header_reader(
+ reinterpret_cast<const char*>(packet.header().begin()),
+ packet.header().size());
+ uint8_t parsed_packet_start_code_prefix[3];
+ EXPECT_TRUE(
+ header_reader.ReadBytes(parsed_packet_start_code_prefix,
+ sizeof(parsed_packet_start_code_prefix)));
+ EXPECT_EQ(0x00u, parsed_packet_start_code_prefix[0]);
+ EXPECT_EQ(0x00u, parsed_packet_start_code_prefix[1]);
+ EXPECT_EQ(0x01u, parsed_packet_start_code_prefix[2]);
+ uint8_t parsed_stream_id;
+ EXPECT_TRUE(header_reader.ReadU8(&parsed_stream_id));
+ EXPECT_EQ(stream_id, parsed_stream_id);
+ uint16_t parsed_packet_length;
+ EXPECT_TRUE(header_reader.ReadU16(&parsed_packet_length));
+ size_t packet_length = static_cast<size_t>(header_reader.remaining()) +
+ packet.unit_header().size() + packet.unit().size();
+ if (packet_length >> 16)
+ packet_length = 0u;
+ EXPECT_EQ(packet_length, parsed_packet_length);
+ uint16_t parsed_flags;
+ EXPECT_TRUE(header_reader.ReadU16(&parsed_flags));
+ EXPECT_EQ(
+ 0u, parsed_flags & ~(pes::kMarkerFlag | pes::kPtsFlag | pes::kDtsFlag));
+ const bool parsed_pts_flag = (parsed_flags & pes::kPtsFlag) != 0u;
+ const bool parsed_dts_flag = (parsed_flags & pes::kDtsFlag) != 0u;
+ EXPECT_EQ(!pts_.is_null(), parsed_pts_flag);
+ EXPECT_EQ(!pts_.is_null() && !dts_.is_null(), parsed_dts_flag);
+ uint8_t parsed_header_length;
+ EXPECT_TRUE(header_reader.ReadU8(&parsed_header_length));
+ EXPECT_EQ(header_reader.remaining(), parsed_header_length);
+ if (parsed_pts_flag) {
+ uint8_t parsed_pts_bytes[5];
+ EXPECT_TRUE(
+ header_reader.ReadBytes(parsed_pts_bytes, sizeof(parsed_pts_bytes)));
+ const uint64_t parsed_pts =
+ ParseTimeStamp(parsed_pts_bytes, parsed_dts_flag ? 0x3u : 0x2u);
+ if (parsed_dts_flag) {
+ uint8_t parsed_dts_bytes[5];
+ EXPECT_TRUE(header_reader.ReadBytes(parsed_dts_bytes,
+ sizeof(parsed_dts_bytes)));
+ const uint64_t parsed_dts = ParseTimeStamp(parsed_dts_bytes, 0x1u);
+ EXPECT_EQ(
+ static_cast<uint64_t>(90 * (pts_ - dts_).InMicroseconds() / 1000),
+ (parsed_pts - parsed_dts) & UINT64_C(0x1FFFFFFFF));
+ }
+ }
+ while (header_reader.remaining() > 0) {
+ uint8_t parsed_stuffing_byte;
+ EXPECT_TRUE(header_reader.ReadU8(&parsed_stuffing_byte));
+ EXPECT_EQ(0xFFu, parsed_stuffing_byte);
+ }
+ EXPECT_EQ(0, header_reader.remaining());
+ }
+
+ void CheckElementaryStreamPacketUnitHeader(
+ const WiFiDisplayElementaryStreamPacket& packet,
+ const uint8_t* unit_header_data,
+ size_t unit_header_size) {
+ EXPECT_EQ(unit_header_data, packet.unit_header().begin());
+ EXPECT_EQ(unit_header_size, packet.unit_header().size());
+ }
+
+ void CheckElementaryStreamPacketUnit(
+ const WiFiDisplayElementaryStreamPacket& packet) {
+ EXPECT_EQ(0u, (packet.header().size() + packet.unit_header().size()) %
+ pes::kUnitDataAlignment);
+ EXPECT_EQ(unit_.data(), packet.unit().begin());
+ EXPECT_EQ(unit_.size(), packet.unit().size());
+ }
+
+ enum { kVideoOnlyUnitSize = 0x8000u }; // Not exact. Be on the safe side.
+
+ const std::vector<uint8_t> unit_;
+ const base::TimeTicks now_;
+ const base::TimeTicks dts_;
+ const base::TimeTicks pts_;
+};
+
+TEST_P(WiFiDisplayElementaryStreamUnitPacketizationTest,
+ EncodeToElementaryStreamPacket) {
+ const size_t kMaxUnitHeaderSize = 4u;
+
+ const uint8_t stream_id =
+ unit_.size() >= kVideoOnlyUnitSize
+ ? WiFiDisplayElementaryStreamPacketizer::kFirstVideoStreamId
+ : WiFiDisplayElementaryStreamPacketizer::kFirstAudioStreamId;
+
+ WiFiDisplayElementaryStreamPacketizer packetizer;
+ uint8_t unit_header_data[kMaxUnitHeaderSize];
+ for (size_t unit_header_size = 0u; unit_header_size <= kMaxUnitHeaderSize;
+ ++unit_header_size) {
+ WiFiDisplayElementaryStreamPacket packet =
+ packetizer.EncodeElementaryStreamUnit(stream_id, unit_header_data,
+ unit_header_size, unit_.data(),
+ unit_.size(), pts_, dts_);
+ CheckElementaryStreamPacketHeader(packet, stream_id);
+ CheckElementaryStreamPacketUnitHeader(packet, unit_header_data,
+ unit_header_size);
+ CheckElementaryStreamPacketUnit(packet);
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(
+ WiFiDisplayElementaryStreamUnitPacketizationTests,
+ WiFiDisplayElementaryStreamUnitPacketizationTest,
+ testing::Combine(testing::Values(123u, 180u, 0x10000u),
+ testing::Values(base::TimeDelta::Max(),
+ base::TimeDelta::FromMicroseconds(0)),
+ testing::Values(base::TimeDelta::Max(),
+ base::TimeDelta::FromMicroseconds(
+ 1000 * INT64_C(0x123456789) / 90))));
+
+} // namespace
+} // namespace extensions
diff --git a/extensions/renderer/api/display_source/wifi_display/wifi_display_stream_packet_part.h b/extensions/renderer/api/display_source/wifi_display/wifi_display_stream_packet_part.h
new file mode 100644
index 0000000..152b4a7
--- /dev/null
+++ b/extensions/renderer/api/display_source/wifi_display/wifi_display_stream_packet_part.h
@@ -0,0 +1,43 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_RENDERER_API_DISPLAY_SOURCE_WIFI_DISPLAY_WIFI_DISPLAY_STREAM_PACKET_PART_H_
+#define EXTENSIONS_RENDERER_API_DISPLAY_SOURCE_WIFI_DISPLAY_WIFI_DISPLAY_STREAM_PACKET_PART_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/macros.h"
+
+namespace extensions {
+
+// WiFi Display elementary stream unit packetization consists of multiple
+// packetization phases. During those phases, unit buffer bytes are not
+// modified but only wrapped in packets.
+// This class allows different kind of WiFi Display stream packets to refer to
+// unit buffer bytes without copying them.
+class WiFiDisplayStreamPacketPart {
+ public:
+ WiFiDisplayStreamPacketPart(const uint8_t* data, size_t size)
+ : data_(data), size_(size) {}
+ template <size_t N>
+ explicit WiFiDisplayStreamPacketPart(const uint8_t (&data)[N])
+ : WiFiDisplayStreamPacketPart(data, N) {}
+
+ const uint8_t* begin() const { return data(); }
+ const uint8_t* data() const { return data_; }
+ bool empty() const { return size() == 0u; }
+ const uint8_t* end() const { return data() + size(); }
+ size_t size() const { return size_; }
+
+ private:
+ const uint8_t* const data_;
+ const size_t size_;
+
+ DISALLOW_COPY_AND_ASSIGN(WiFiDisplayStreamPacketPart);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_RENDERER_API_DISPLAY_SOURCE_WIFI_DISPLAY_WIFI_DISPLAY_STREAM_PACKET_BASE_H_