diff options
author | eero.hakkinen <eero.hakkinen@intel.com> | 2016-03-18 05:51:52 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-03-18 12:53:09 +0000 |
commit | dc1557a2f96a9a8d85eec584ae22176352aee13d (patch) | |
tree | 9fedbbf846f3355640d9489c5ad4e794ba882eb3 /extensions/renderer | |
parent | eb25561b784aeea6bc86aeb0621e2bff71d05770 (diff) | |
download | chromium_src-dc1557a2f96a9a8d85eec584ae22176352aee13d.zip chromium_src-dc1557a2f96a9a8d85eec584ae22176352aee13d.tar.gz chromium_src-dc1557a2f96a9a8d85eec584ae22176352aee13d.tar.bz2 |
[chrome.displaySource] Implement elementary stream packetizer.
The WiFi Display elementary stream packetizer packetizes unit buffers to
Packetized Elementary Stream (PES) packets. It will be used internally by
a WiFi Display transport stream packetizer.
This is part of a WiFi Display packetizer patch series:
* https://codereview.chromium.org/1796123002/ <-- this CL
WiFi Display elementary stream packetizer
* https://codereview.chromium.org/1800493003/
WiFi Display elementary stream descriptors
* https://codereview.chromium.org/1797953002/
WiFi Display transport stream packetizer
* https://codereview.chromium.org/1796073003/
WiFi Display media packetizer
BUG=242107
Review URL: https://codereview.chromium.org/1796123002
Cr-Commit-Position: refs/heads/master@{#381945}
Diffstat (limited to 'extensions/renderer')
4 files changed, 469 insertions, 0 deletions
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_ |