summaryrefslogtreecommitdiffstats
path: root/extensions
diff options
context:
space:
mode:
authoreero.hakkinen <eero.hakkinen@intel.com>2016-03-22 15:15:16 -0700
committerCommit bot <commit-bot@chromium.org>2016-03-22 22:16:38 +0000
commitdb33431413fe39fca0a34381092301d91ca66f40 (patch)
tree9ffc2d7dca1cb73c2000a73013a07df503025537 /extensions
parent799324ac96cb110489e0b58e367615779a256095 (diff)
downloadchromium_src-db33431413fe39fca0a34381092301d91ca66f40.zip
chromium_src-db33431413fe39fca0a34381092301d91ca66f40.tar.gz
chromium_src-db33431413fe39fca0a34381092301d91ca66f40.tar.bz2
[chrome.displaySource] Implement transport stream packetizer.
The WiFi Display transport stream packetizer packetizes unit buffers first to Packetized Elementary Stream (PES) packets (using a WiFi Display elementary stream packetizer) and then to Transport Stream (TS) packets. It will be used as a base class for a WiFi Display media packetizer. This is part of a WiFi Display packetizer patch series: * https://codereview.chromium.org/1796123002/ WiFi Display elementary stream packetizer * https://codereview.chromium.org/1800493003/ WiFi Display elementary stream descriptors * https://codereview.chromium.org/1797953002/ <-- this CL WiFi Display transport stream packetizer * https://codereview.chromium.org/1796073003/ WiFi Display media packetizer BUG=242107 Review URL: https://codereview.chromium.org/1797953002 Cr-Commit-Position: refs/heads/master@{#382706}
Diffstat (limited to 'extensions')
-rw-r--r--extensions/extensions.gypi4
-rw-r--r--extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_info.cc46
-rw-r--r--extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_info.h58
-rw-r--r--extensions/renderer/api/display_source/wifi_display/wifi_display_media_packetizer_unittest.cc548
-rw-r--r--extensions/renderer/api/display_source/wifi_display/wifi_display_transport_stream_packetizer.cc727
-rw-r--r--extensions/renderer/api/display_source/wifi_display/wifi_display_transport_stream_packetizer.h176
6 files changed, 1559 insertions, 0 deletions
diff --git a/extensions/extensions.gypi b/extensions/extensions.gypi
index e1c30bf..4ecff0d 100644
--- a/extensions/extensions.gypi
+++ b/extensions/extensions.gypi
@@ -1038,6 +1038,8 @@
'extensions_renderer_sources_wifi_display': [
'renderer/api/display_source/wifi_display/wifi_display_elementary_stream_descriptor.cc',
'renderer/api/display_source/wifi_display/wifi_display_elementary_stream_descriptor.h',
+ 'renderer/api/display_source/wifi_display/wifi_display_elementary_stream_info.cc',
+ 'renderer/api/display_source/wifi_display/wifi_display_elementary_stream_info.h',
'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',
@@ -1045,6 +1047,8 @@
'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',
+ 'renderer/api/display_source/wifi_display/wifi_display_transport_stream_packetizer.cc',
+ 'renderer/api/display_source/wifi_display/wifi_display_transport_stream_packetizer.h',
],
'extensions_utility_sources': [
'utility/unpacker.cc',
diff --git a/extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_info.cc b/extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_info.cc
new file mode 100644
index 0000000..2c84885
--- /dev/null
+++ b/extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_info.cc
@@ -0,0 +1,46 @@
+// 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_info.h"
+
+#include <utility>
+
+namespace extensions {
+
+WiFiDisplayElementaryStreamInfo::WiFiDisplayElementaryStreamInfo(
+ ElementaryStreamType type)
+ : type_(type) {}
+
+WiFiDisplayElementaryStreamInfo::WiFiDisplayElementaryStreamInfo(
+ ElementaryStreamType type,
+ DescriptorVector descriptors)
+ : descriptors_(std::move(descriptors)), type_(type) {}
+
+WiFiDisplayElementaryStreamInfo::WiFiDisplayElementaryStreamInfo(
+ const WiFiDisplayElementaryStreamInfo&) = default;
+
+WiFiDisplayElementaryStreamInfo::WiFiDisplayElementaryStreamInfo(
+ WiFiDisplayElementaryStreamInfo&&) = default;
+
+WiFiDisplayElementaryStreamInfo::~WiFiDisplayElementaryStreamInfo() {}
+
+WiFiDisplayElementaryStreamInfo& WiFiDisplayElementaryStreamInfo::operator=(
+ WiFiDisplayElementaryStreamInfo&&) = default;
+
+void WiFiDisplayElementaryStreamInfo::AddDescriptor(
+ WiFiDisplayElementaryStreamDescriptor descriptor) {
+ descriptors_.emplace_back(std::move(descriptor));
+}
+
+const WiFiDisplayElementaryStreamDescriptor*
+WiFiDisplayElementaryStreamInfo::FindDescriptor(
+ DescriptorTag descriptor_tag) const {
+ for (const auto& descriptor : descriptors()) {
+ if (descriptor.tag() == descriptor_tag)
+ return &descriptor;
+ }
+ return nullptr;
+}
+
+} // namespace extensions
diff --git a/extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_info.h b/extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_info.h
new file mode 100644
index 0000000..a0e63a8
--- /dev/null
+++ b/extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_info.h
@@ -0,0 +1,58 @@
+// 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_INFO_H_
+#define EXTENSIONS_RENDERER_API_DISPLAY_SOURCE_WIFI_DISPLAY_WIFI_DISPLAY_ELEMENTARY_STREAM_INFO_H_
+
+#include <stdint.h>
+#include <vector>
+
+#include "extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_descriptor.h"
+
+namespace extensions {
+
+// WiFi Display elementary stream info is a container for elementary stream
+// information and is used for passing that information to a WiFi Display
+// transport stream packetizer.
+class WiFiDisplayElementaryStreamInfo {
+ public:
+ using DescriptorVector = std::vector<WiFiDisplayElementaryStreamDescriptor>;
+ using DescriptorTag = WiFiDisplayElementaryStreamDescriptor::DescriptorTag;
+
+ enum ElementaryStreamType : uint8_t {
+ AUDIO_AAC = 0x0Fu,
+ AUDIO_AC3 = 0x81u,
+ AUDIO_LPCM = 0x83u,
+ VIDEO_H264 = 0x1Bu,
+ };
+
+ explicit WiFiDisplayElementaryStreamInfo(ElementaryStreamType type);
+ WiFiDisplayElementaryStreamInfo(ElementaryStreamType type,
+ DescriptorVector descriptors);
+ WiFiDisplayElementaryStreamInfo(const WiFiDisplayElementaryStreamInfo&);
+ WiFiDisplayElementaryStreamInfo(WiFiDisplayElementaryStreamInfo&&);
+ ~WiFiDisplayElementaryStreamInfo();
+
+ WiFiDisplayElementaryStreamInfo& operator=(WiFiDisplayElementaryStreamInfo&&);
+
+ const DescriptorVector& descriptors() const { return descriptors_; }
+ ElementaryStreamType type() const { return type_; }
+
+ void AddDescriptor(WiFiDisplayElementaryStreamDescriptor descriptor);
+ const WiFiDisplayElementaryStreamDescriptor* FindDescriptor(
+ DescriptorTag descriptor_tag) const;
+ template <typename Descriptor>
+ const Descriptor* FindDescriptor() const {
+ return static_cast<const Descriptor*>(
+ FindDescriptor(static_cast<DescriptorTag>(Descriptor::kTag)));
+ }
+
+ private:
+ DescriptorVector descriptors_;
+ ElementaryStreamType type_;
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_RENDERER_API_DISPLAY_SOURCE_WIFI_DISPLAY_WIFI_DISPLAY_ELEMENTARY_STREAM_INFO_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
index a8b769a..4ba1969 100644
--- 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
@@ -2,12 +2,37 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <list>
+
#include "base/big_endian.h"
+#include "extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_descriptor.h"
+#include "extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_info.h"
#include "extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_packetizer.h"
+#include "extensions/renderer/api/display_source/wifi_display/wifi_display_transport_stream_packetizer.h"
#include "testing/gtest/include/gtest/gtest.h"
+using PacketPart = extensions::WiFiDisplayStreamPacketPart;
+
namespace extensions {
+std::ostream& operator<<(std::ostream& os, const PacketPart& part) {
+ const auto flags = os.flags();
+ os << "{" << std::hex << std::noshowbase;
+ for (const auto& item : part) {
+ if (&item != &*part.begin())
+ os << ", ";
+ os << "0x" << static_cast<unsigned>(item);
+ }
+ os.setf(flags, std::ios::basefield | std::ios::showbase);
+ return os << "}";
+}
+
+bool operator==(const PacketPart& a, const PacketPart& b) {
+ if (a.size() != b.size())
+ return false;
+ return std::equal(a.begin(), a.end(), b.begin());
+}
+
namespace {
namespace pes {
@@ -17,6 +42,108 @@ const unsigned kPtsFlag = 0x0080u;
const size_t kUnitDataAlignment = sizeof(uint32_t);
}
+namespace ts {
+const uint64_t kTimeStampMask = (static_cast<uint64_t>(1u) << 33) - 1u;
+const uint64_t kTimeStampSecond = 90000u; // 90 kHz
+const uint64_t kProgramClockReferenceSecond =
+ 300u * kTimeStampSecond; // 27 MHz
+
+// Packet header:
+const size_t kPacketHeaderSize = 4u;
+const unsigned kSyncByte = 0x47u;
+const uint32_t kSyncByteMask = 0xFF000000u;
+const uint32_t kTransportErrorIndicator = 0x00800000u;
+const uint32_t kPayloadUnitStartIndicator = 0x00400000u;
+const uint32_t kTransportPriority = 0x00200000u;
+const uint32_t kScramblingControlMask = 0x000000C0u;
+const uint32_t kAdaptationFieldFlag = 0x00000020u;
+const uint32_t kPayloadFlag = 0x00000010u;
+
+// Adaptation field:
+const unsigned kRandomAccessFlag = 0x40u;
+const unsigned kPcrFlag = 0x10u;
+} // namespace ts
+
+namespace widi {
+const unsigned kProgramAssociationTablePacketId = 0x0000u;
+const unsigned kProgramMapTablePacketId = 0x0100u;
+const unsigned kProgramClockReferencePacketId = 0x1000u;
+const unsigned kVideoStreamPacketId = 0x1011u;
+const unsigned kFirstAudioStreamPacketId = 0x1100u;
+} // namespace widi
+
+template <typename PacketContainer>
+class PacketCollector {
+ public:
+ PacketContainer FetchPackets() {
+ PacketContainer container;
+ container.swap(packets_);
+ return container;
+ }
+
+ protected:
+ PacketContainer packets_;
+};
+
+class FakeTransportStreamPacketizer
+ : public WiFiDisplayTransportStreamPacketizer,
+ public PacketCollector<std::list<WiFiDisplayTransportStreamPacket>> {
+ public:
+ FakeTransportStreamPacketizer(
+ const base::TimeDelta& delay_for_unit_time_stamps,
+ std::vector<WiFiDisplayElementaryStreamInfo> stream_infos)
+ : WiFiDisplayTransportStreamPacketizer(delay_for_unit_time_stamps,
+ std::move(stream_infos)) {}
+
+ using WiFiDisplayTransportStreamPacketizer::NormalizeUnitTimeStamps;
+
+ protected:
+ bool OnPacketizedTransportStreamPacket(
+ const WiFiDisplayTransportStreamPacket& transport_stream_packet,
+ bool flush) override {
+ // Make a copy of header bytes as they are in stack.
+ headers_.emplace_back(transport_stream_packet.header().begin(),
+ transport_stream_packet.header().end());
+ const auto& header = headers_.back();
+ if (transport_stream_packet.payload().empty()) {
+ packets_.emplace_back(header.data(), header.size());
+ } else {
+ packets_.emplace_back(header.data(), header.size(),
+ transport_stream_packet.payload().begin());
+ }
+ EXPECT_EQ(transport_stream_packet.header().size(),
+ packets_.back().header().size());
+ EXPECT_EQ(transport_stream_packet.payload().size(),
+ packets_.back().payload().size());
+ EXPECT_EQ(transport_stream_packet.filler().size(),
+ packets_.back().filler().size());
+ return true;
+ }
+
+ private:
+ std::vector<std::vector<uint8_t>> headers_;
+};
+
+struct ProgramClockReference {
+ enum { kInvalidBase = ~static_cast<uint64_t>(0u) };
+ uint64_t base;
+ uint16_t extension;
+};
+
+ProgramClockReference ParseProgramClockReference(const uint8_t pcr_bytes[6]) {
+ const uint8_t reserved_pcr_bits = pcr_bytes[4] & 0x7Eu;
+ EXPECT_EQ(0x7Eu, reserved_pcr_bits);
+ ProgramClockReference pcr;
+ pcr.base = pcr_bytes[0];
+ pcr.base = (pcr.base << 8) | pcr_bytes[1];
+ pcr.base = (pcr.base << 8) | pcr_bytes[2];
+ pcr.base = (pcr.base << 8) | pcr_bytes[3];
+ pcr.base = (pcr.base << 1) | ((pcr_bytes[4] & 0x80u) >> 7);
+ pcr.extension = pcr_bytes[4] & 0x01u;
+ pcr.extension = (pcr.extension << 8) | pcr_bytes[5];
+ return pcr;
+}
+
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);
@@ -31,6 +158,14 @@ uint64_t ParseTimeStamp(const uint8_t ts_bytes[5], uint8_t pts_dts_indicator) {
return ts;
}
+unsigned ParseTransportStreamPacketId(
+ const WiFiDisplayTransportStreamPacket& packet) {
+ if (packet.header().size() < ts::kPacketHeaderSize)
+ return ~0u;
+ return (((packet.header().begin()[1] & 0x001Fu) << 8) |
+ packet.header().begin()[2]);
+}
+
class WiFiDisplayElementaryStreamUnitPacketizationTest
: public testing::TestWithParam<
testing::tuple<unsigned, base::TimeDelta, base::TimeDelta>> {
@@ -120,12 +255,268 @@ class WiFiDisplayElementaryStreamUnitPacketizationTest
EXPECT_EQ(unit_.size(), packet.unit().size());
}
+ void CheckTransportStreamPacketHeader(
+ base::BigEndianReader* header_reader,
+ bool expected_payload_unit_start_indicator,
+ unsigned expected_packet_id,
+ bool* adaptation_field_flag,
+ uint8_t expected_continuity_counter) {
+ uint32_t parsed_u32;
+ EXPECT_TRUE(header_reader->ReadU32(&parsed_u32));
+ EXPECT_EQ(ts::kSyncByte << 24u, parsed_u32 & ts::kSyncByteMask);
+ EXPECT_EQ(0x0u, parsed_u32 & ts::kTransportErrorIndicator);
+ EXPECT_EQ(expected_payload_unit_start_indicator,
+ (parsed_u32 & ts::kPayloadUnitStartIndicator) != 0u);
+ EXPECT_EQ(0x0u, parsed_u32 & ts::kTransportPriority);
+ EXPECT_EQ(expected_packet_id, (parsed_u32 & 0x001FFF00) >> 8);
+ EXPECT_EQ(0x0u, parsed_u32 & ts::kScramblingControlMask);
+ if (!adaptation_field_flag) {
+ EXPECT_EQ(0x0u, parsed_u32 & ts::kAdaptationFieldFlag);
+ } else {
+ *adaptation_field_flag = (parsed_u32 & ts::kAdaptationFieldFlag) != 0u;
+ }
+ EXPECT_EQ(ts::kPayloadFlag, parsed_u32 & ts::kPayloadFlag);
+ EXPECT_EQ(expected_continuity_counter & 0xFu, parsed_u32 & 0x0000000Fu);
+ }
+
+ void CheckTransportStreamAdaptationField(
+ base::BigEndianReader* header_reader,
+ const WiFiDisplayTransportStreamPacket& packet,
+ uint8_t* adaptation_field_flags) {
+ uint8_t parsed_adaptation_field_length;
+ EXPECT_TRUE(header_reader->ReadU8(&parsed_adaptation_field_length));
+ if (parsed_adaptation_field_length > 0u) {
+ const int initial_remaining = header_reader->remaining();
+ uint8_t parsed_adaptation_field_flags;
+ EXPECT_TRUE(header_reader->ReadU8(&parsed_adaptation_field_flags));
+ if (!adaptation_field_flags) {
+ EXPECT_EQ(0x0u, parsed_adaptation_field_flags);
+ } else {
+ *adaptation_field_flags = parsed_adaptation_field_flags;
+ if (parsed_adaptation_field_flags & ts::kPcrFlag) {
+ uint8_t parsed_pcr_bytes[6];
+ EXPECT_TRUE(header_reader->ReadBytes(parsed_pcr_bytes,
+ sizeof(parsed_pcr_bytes)));
+ parsed_pcr_ = ParseProgramClockReference(parsed_pcr_bytes);
+ }
+ }
+ size_t remaining_stuffing_length =
+ parsed_adaptation_field_length -
+ static_cast<size_t>(initial_remaining - header_reader->remaining());
+ while (remaining_stuffing_length > 0u && header_reader->remaining() > 0) {
+ // Adaptation field stuffing byte in header_reader.
+ uint8_t parsed_stuffing_byte;
+ EXPECT_TRUE(header_reader->ReadU8(&parsed_stuffing_byte));
+ EXPECT_EQ(0xFFu, parsed_stuffing_byte);
+ --remaining_stuffing_length;
+ }
+ if (packet.payload().empty()) {
+ // Adaptation field stuffing bytes in packet.filler().
+ EXPECT_EQ(remaining_stuffing_length, packet.filler().size());
+ EXPECT_EQ(0xFFu, packet.filler().value());
+ } else {
+ EXPECT_EQ(0u, remaining_stuffing_length);
+ }
+ }
+ }
+
+ void CheckTransportStreamProgramAssociationTablePacket(
+ const WiFiDisplayTransportStreamPacket& packet) {
+ static const uint8_t kProgramAssicationTable[4u + 13u] = {
+ // Pointer:
+ 0u, // Pointer field
+ // Table header:
+ 0x00u, // Table ID (PAT)
+ 0x80u | // Section syntax indicator (0b1 for PAT)
+ 0x00u | // Private bit (0b0 for PAT)
+ 0x30u | // Reserved bits (0b11)
+ 0x00u | // Section length unused bits (0b00)
+ 0u, // Section length (10 bits)
+ 13u, //
+ // Table syntax:
+ 0x00u, // Table ID extension (transport stream ID)
+ 0x01u, //
+ 0xC0u | // Reserved bits (0b11)
+ 0x00u | // Version (0b00000)
+ 0x01u, // Current indicator (0b1)
+ 0u, // Section number
+ 0u, // Last section number
+ // Program association table specific data:
+ 0x00u, // Program number
+ 0x01u, //
+ 0xE0 | // Reserved bits (0b111)
+ 0x01u, // Program map packet ID (13 bits)
+ 0x00, //
+ // CRC:
+ 0xE8u,
+ 0xF9u, 0x5Eu, 0x7Du};
+
+ base::BigEndianReader header_reader(
+ reinterpret_cast<const char*>(packet.header().begin()),
+ packet.header().size());
+
+ CheckTransportStreamPacketHeader(
+ &header_reader, true, widi::kProgramAssociationTablePacketId, nullptr,
+ continuity_.program_assication_table++);
+
+ EXPECT_EQ(PacketPart(kProgramAssicationTable),
+ PacketPart(packet.header().end() - header_reader.remaining(),
+ static_cast<size_t>(header_reader.remaining())));
+ EXPECT_TRUE(header_reader.Skip(header_reader.remaining()));
+
+ EXPECT_EQ(0, header_reader.remaining());
+ EXPECT_EQ(0u, packet.payload().size());
+ }
+
+ void CheckTransportStreamProgramMapTablePacket(
+ const WiFiDisplayTransportStreamPacket& packet,
+ const PacketPart& program_map_table) {
+ base::BigEndianReader header_reader(
+ reinterpret_cast<const char*>(packet.header().begin()),
+ packet.header().size());
+
+ CheckTransportStreamPacketHeader(&header_reader, true,
+ widi::kProgramMapTablePacketId, nullptr,
+ continuity_.program_map_table++);
+
+ EXPECT_EQ(program_map_table,
+ PacketPart(packet.header().end() - header_reader.remaining(),
+ static_cast<size_t>(header_reader.remaining())));
+ EXPECT_TRUE(header_reader.Skip(header_reader.remaining()));
+
+ EXPECT_EQ(0, header_reader.remaining());
+ EXPECT_EQ(0u, packet.payload().size());
+ }
+
+ void CheckTransportStreamProgramClockReferencePacket(
+ const WiFiDisplayTransportStreamPacket& packet) {
+ base::BigEndianReader header_reader(
+ reinterpret_cast<const char*>(packet.header().begin()),
+ packet.header().size());
+
+ bool parsed_adaptation_field_flag;
+ CheckTransportStreamPacketHeader(
+ &header_reader, true, widi::kProgramClockReferencePacketId,
+ &parsed_adaptation_field_flag, continuity_.program_clock_reference++);
+ EXPECT_TRUE(parsed_adaptation_field_flag);
+
+ uint8_t parsed_adaptation_field_flags;
+ CheckTransportStreamAdaptationField(&header_reader, packet,
+ &parsed_adaptation_field_flags);
+ EXPECT_EQ(ts::kPcrFlag, parsed_adaptation_field_flags);
+
+ EXPECT_EQ(0, header_reader.remaining());
+ EXPECT_EQ(0u, packet.payload().size());
+ }
+
+ void CheckTransportStreamElementaryStreamPacket(
+ const WiFiDisplayTransportStreamPacket& packet,
+ const WiFiDisplayElementaryStreamPacket& elementary_stream_packet,
+ unsigned stream_index,
+ unsigned expected_packet_id,
+ bool expected_random_access,
+ const uint8_t** unit_data_pos) {
+ const bool first_transport_stream_packet_for_current_unit =
+ packet.payload().begin() == unit_.data();
+ const bool last_transport_stream_packet_for_current_unit =
+ packet.payload().end() == unit_.data() + unit_.size();
+ base::BigEndianReader header_reader(
+ reinterpret_cast<const char*>(packet.header().begin()),
+ packet.header().size());
+
+ bool parsed_adaptation_field_flag;
+ CheckTransportStreamPacketHeader(
+ &header_reader, first_transport_stream_packet_for_current_unit,
+ expected_packet_id, &parsed_adaptation_field_flag,
+ continuity_.elementary_streams[stream_index]++);
+
+ if (first_transport_stream_packet_for_current_unit) {
+ // Random access can only be signified by adaptation field.
+ if (expected_random_access)
+ EXPECT_TRUE(parsed_adaptation_field_flag);
+ // If there is no need for padding nor for a random access indicator,
+ // then there is no need for an adaptation field, either.
+ if (!last_transport_stream_packet_for_current_unit &&
+ !expected_random_access) {
+ EXPECT_FALSE(parsed_adaptation_field_flag);
+ }
+ if (parsed_adaptation_field_flag) {
+ uint8_t parsed_adaptation_field_flags;
+ CheckTransportStreamAdaptationField(&header_reader, packet,
+ &parsed_adaptation_field_flags);
+ EXPECT_EQ(expected_random_access ? ts::kRandomAccessFlag : 0u,
+ parsed_adaptation_field_flags);
+ }
+
+ // Elementary stream header.
+ PacketPart parsed_elementary_stream_packet_header(
+ packet.header().end() - header_reader.remaining(),
+ std::min(elementary_stream_packet.header().size(),
+ static_cast<size_t>(header_reader.remaining())));
+ EXPECT_EQ(elementary_stream_packet.header(),
+ parsed_elementary_stream_packet_header);
+ EXPECT_TRUE(
+ header_reader.Skip(parsed_elementary_stream_packet_header.size()));
+
+ // Elementary stream unit header.
+ PacketPart parsed_unit_header(
+ packet.header().end() - header_reader.remaining(),
+ std::min(elementary_stream_packet.unit_header().size(),
+ static_cast<size_t>(header_reader.remaining())));
+ EXPECT_EQ(elementary_stream_packet.unit_header(), parsed_unit_header);
+ EXPECT_TRUE(header_reader.Skip(parsed_unit_header.size()));
+
+ // Time stamps.
+ if (parsed_elementary_stream_packet_header.size() >= 19u) {
+ uint64_t parsed_dts = ParseTimeStamp(
+ &parsed_elementary_stream_packet_header.begin()[14], 0x1u);
+ // Check that
+ // 0 <= 300 * parsed_dts - parsed_pcr_value <=
+ // kProgramClockReferenceSecond
+ // where
+ // parsed_pcr_value = 300 * parsed_pcr_.base + parsed_pcr_.extension
+ // but allow parsed_pcr_.base and parsed_dts to wrap around in 33 bits.
+ EXPECT_NE(ProgramClockReference::kInvalidBase, parsed_pcr_.base);
+ EXPECT_LE(
+ 300u * ((parsed_dts - parsed_pcr_.base) & ts::kTimeStampMask) -
+ parsed_pcr_.extension,
+ ts::kProgramClockReferenceSecond)
+ << " DTS must be not smaller than PCR!";
+ }
+ } else {
+ // If there is no need for padding, then there is no need for
+ // an adaptation field, either.
+ if (!last_transport_stream_packet_for_current_unit)
+ EXPECT_FALSE(parsed_adaptation_field_flag);
+ if (parsed_adaptation_field_flag) {
+ CheckTransportStreamAdaptationField(&header_reader, packet, nullptr);
+ }
+ }
+ EXPECT_EQ(0, header_reader.remaining());
+
+ // Transport stream packet payload.
+ EXPECT_EQ(*unit_data_pos, packet.payload().begin());
+ if (*unit_data_pos == packet.payload().begin())
+ *unit_data_pos += packet.payload().size();
+
+ // Transport stream packet filler.
+ EXPECT_EQ(0u, packet.filler().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_;
+
+ struct {
+ size_t program_assication_table;
+ size_t program_map_table;
+ size_t program_clock_reference;
+ size_t elementary_streams[3];
+ } continuity_ = {0u, 0u, 0u, {0u, 0u, 0u}};
+ ProgramClockReference parsed_pcr_ = {ProgramClockReference::kInvalidBase, 0u};
};
TEST_P(WiFiDisplayElementaryStreamUnitPacketizationTest,
@@ -152,6 +543,163 @@ TEST_P(WiFiDisplayElementaryStreamUnitPacketizationTest,
}
}
+TEST_P(WiFiDisplayElementaryStreamUnitPacketizationTest,
+ EncodeToTransportStreamPackets) {
+ enum { kStreamCount = 3u };
+ static const bool kBoolValues[] = {false, true};
+ static const unsigned kPacketIds[kStreamCount] = {
+ widi::kVideoStreamPacketId, widi::kFirstAudioStreamPacketId + 0u,
+ widi::kFirstAudioStreamPacketId + 1u};
+ static const uint8_t kProgramMapTable[4u + 42u] = {
+ // Pointer:
+ 0u, // Pointer field
+ // Table header:
+ 0x02u, // Table ID (PMT)
+ 0x80u | // Section syntax indicator (0b1 for PMT)
+ 0x00u | // Private bit (0b0 for PMT)
+ 0x30u | // Reserved bits (0b11)
+ 0x00u | // Section length unused bits (0b00)
+ 0u, // Section length (10 bits)
+ 42u, //
+ // Table syntax:
+ 0x00u, // Table ID extension (program number)
+ 0x01u, //
+ 0xC0u | // Reserved bits (0b11)
+ 0x00u | // Version (0b00000)
+ 0x01u, // Current indicator (0b1)
+ 0u, // Section number
+ 0u, // Last section number
+ // Program map table specific data:
+ 0xE0u | // Reserved bits (0b111)
+ 0x10u, // Program clock reference packet ID (13 bits)
+ 0x00u, //
+ 0xF0u | // Reserved bits (0b11)
+ 0x00u | // Program info length unused bits
+ 0u, // Program info length (10 bits)
+ 0u, //
+ // Elementary stream specific data:
+ 0x1Bu, // Stream type (H.264 in a packetized stream)
+ 0xE0u | // Reserved bits (0b111)
+ 0x10u, // Elementary packet ID (13 bits)
+ 0x11u, //
+ 0xF0u | // Reserved bits (0b1111)
+ 0x00u | // Elementary stream info length unused bits
+ 0u, // Elementary stream info length (10 bits)
+ 10u, //
+ 0x28u, // AVC video descriptor tag
+ 4u, // Descriptor length
+ 0xA5u,
+ 0xF5u, 0xBDu, 0xBFu,
+ 0x2Au, // AVC timing and HRD descriptor tag
+ 2u, // Descriptor length
+ 0x7Eu, 0x1Fu,
+ // Elementary stream specific data:
+ 0x83u, // Stream type (lossless audio in a packetized stream)
+ 0xE0u | // Reserved bits (0b111)
+ 0x11u, // Elementary packet ID (13 bits)
+ 0x00u, //
+ 0xF0u | // Reserved bits (0b1111)
+ 0x00u | // Elementary stream info length unused bits
+ 0u, // Elementary stream info length (10 bits)
+ 4u, //
+ 0x83u, // LPCM audio stream descriptor tag
+ 2u, // Descriptor length
+ 0x26u,
+ 0x2Fu,
+ // Elementary stream specific data:
+ 0x0Fu, // Stream type (AAC in a packetized stream)
+ 0xE0u | // Reserved bits (0b111)
+ 0x11u, // Elementary packet ID (13 bits)
+ 0x01u, //
+ 0xF0u | // Reserved bits (0b1111)
+ 0x00u | // Elementary stream info length unused bits
+ 0u, // Elementary stream info length (10 bits)
+ 0u, //
+ // CRC:
+ 0x4Fu,
+ 0x63u, 0xABu, 0x6Eu};
+ static const uint8_t kStreamIds[] = {
+ WiFiDisplayElementaryStreamPacketizer::kFirstVideoStreamId,
+ WiFiDisplayElementaryStreamPacketizer::kPrivateStream1Id,
+ WiFiDisplayElementaryStreamPacketizer::kFirstAudioStreamId};
+
+ using ESDescriptor = WiFiDisplayElementaryStreamDescriptor;
+ std::vector<ESDescriptor> lpcm_descriptors;
+ lpcm_descriptors.emplace_back(ESDescriptor::LPCMAudioStream::Create(
+ ESDescriptor::LPCMAudioStream::SAMPLING_FREQUENCY_44_1K,
+ ESDescriptor::LPCMAudioStream::BITS_PER_SAMPLE_16, false,
+ ESDescriptor::LPCMAudioStream::NUMBER_OF_CHANNELS_STEREO));
+ std::vector<ESDescriptor> video_desciptors;
+ video_desciptors.emplace_back(ESDescriptor::AVCVideo::Create(
+ 0xA5u, true, true, true, 0x15u, 0xBDu, true));
+ video_desciptors.emplace_back(ESDescriptor::AVCTimingAndHRD::Create());
+ std::vector<WiFiDisplayElementaryStreamInfo> stream_infos;
+ stream_infos.emplace_back(WiFiDisplayElementaryStreamInfo::VIDEO_H264,
+ std::move(video_desciptors));
+ stream_infos.emplace_back(WiFiDisplayElementaryStreamInfo::AUDIO_LPCM,
+ std::move(lpcm_descriptors));
+ stream_infos.emplace_back(WiFiDisplayElementaryStreamInfo::AUDIO_AAC);
+ WiFiDisplayElementaryStreamPacketizer elementary_stream_packetizer;
+ FakeTransportStreamPacketizer packetizer(
+ base::TimeDelta::FromMilliseconds(200), std::move(stream_infos));
+
+ size_t packet_index = 0u;
+ for (unsigned stream_index = 0; stream_index < kStreamCount; ++stream_index) {
+ const uint8_t* unit_header_data = nullptr;
+ size_t unit_header_size = 0u;
+ if (stream_index > 0u) { // Audio stream.
+ if (unit_.size() >= kVideoOnlyUnitSize)
+ continue;
+ if (stream_index == 1u) { // LPCM
+ unit_header_data = reinterpret_cast<const uint8_t*>("\xA0\x06\x00\x09");
+ unit_header_size = 4u;
+ }
+ }
+ for (const bool random_access : kBoolValues) {
+ EXPECT_TRUE(packetizer.EncodeElementaryStreamUnit(
+ stream_index, unit_.data(), unit_.size(), random_access, pts_, dts_,
+ true));
+ auto normalized_pts = pts_;
+ auto normalized_dts = dts_;
+ packetizer.NormalizeUnitTimeStamps(&normalized_pts, &normalized_dts);
+ WiFiDisplayElementaryStreamPacket elementary_stream_packet =
+ elementary_stream_packetizer.EncodeElementaryStreamUnit(
+ kStreamIds[stream_index], unit_header_data, unit_header_size,
+ unit_.data(), unit_.size(), normalized_pts, normalized_dts);
+
+ const uint8_t* unit_data_pos = unit_.data();
+ for (const auto& packet : packetizer.FetchPackets()) {
+ switch (ParseTransportStreamPacketId(packet)) {
+ case widi::kProgramAssociationTablePacketId:
+ if (packet_index < 4u)
+ EXPECT_EQ(0u, packet_index);
+ CheckTransportStreamProgramAssociationTablePacket(packet);
+ break;
+ case widi::kProgramMapTablePacketId:
+ if (packet_index < 4u)
+ EXPECT_EQ(1u, packet_index);
+ CheckTransportStreamProgramMapTablePacket(
+ packet, PacketPart(kProgramMapTable));
+ break;
+ case widi::kProgramClockReferencePacketId:
+ if (packet_index < 4u)
+ EXPECT_EQ(2u, packet_index);
+ CheckTransportStreamProgramClockReferencePacket(packet);
+ break;
+ default:
+ if (packet_index < 4u)
+ EXPECT_EQ(3u, packet_index);
+ CheckTransportStreamElementaryStreamPacket(
+ packet, elementary_stream_packet, stream_index,
+ kPacketIds[stream_index], random_access, &unit_data_pos);
+ }
+ ++packet_index;
+ }
+ EXPECT_EQ(unit_.data() + unit_.size(), unit_data_pos);
+ }
+ }
+}
+
INSTANTIATE_TEST_CASE_P(
WiFiDisplayElementaryStreamUnitPacketizationTests,
WiFiDisplayElementaryStreamUnitPacketizationTest,
diff --git a/extensions/renderer/api/display_source/wifi_display/wifi_display_transport_stream_packetizer.cc b/extensions/renderer/api/display_source/wifi_display/wifi_display_transport_stream_packetizer.cc
new file mode 100644
index 0000000..eee0a52
--- /dev/null
+++ b/extensions/renderer/api/display_source/wifi_display/wifi_display_transport_stream_packetizer.cc
@@ -0,0 +1,727 @@
+// 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_transport_stream_packetizer.h"
+
+#include <algorithm>
+#include <cstring>
+#include <utility>
+
+#include "base/logging.h"
+#include "extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_descriptor.h"
+#include "extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_info.h"
+#include "extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_packetizer.h"
+
+namespace extensions {
+namespace {
+
+uint32_t crc32(uint32_t crc, const uint8_t* data, size_t len) {
+ static const uint32_t table[256] = {
+ 0x00000000, 0xb71dc104, 0x6e3b8209, 0xd926430d, 0xdc760413, 0x6b6bc517,
+ 0xb24d861a, 0x0550471e, 0xb8ed0826, 0x0ff0c922, 0xd6d68a2f, 0x61cb4b2b,
+ 0x649b0c35, 0xd386cd31, 0x0aa08e3c, 0xbdbd4f38, 0x70db114c, 0xc7c6d048,
+ 0x1ee09345, 0xa9fd5241, 0xacad155f, 0x1bb0d45b, 0xc2969756, 0x758b5652,
+ 0xc836196a, 0x7f2bd86e, 0xa60d9b63, 0x11105a67, 0x14401d79, 0xa35ddc7d,
+ 0x7a7b9f70, 0xcd665e74, 0xe0b62398, 0x57abe29c, 0x8e8da191, 0x39906095,
+ 0x3cc0278b, 0x8bdde68f, 0x52fba582, 0xe5e66486, 0x585b2bbe, 0xef46eaba,
+ 0x3660a9b7, 0x817d68b3, 0x842d2fad, 0x3330eea9, 0xea16ada4, 0x5d0b6ca0,
+ 0x906d32d4, 0x2770f3d0, 0xfe56b0dd, 0x494b71d9, 0x4c1b36c7, 0xfb06f7c3,
+ 0x2220b4ce, 0x953d75ca, 0x28803af2, 0x9f9dfbf6, 0x46bbb8fb, 0xf1a679ff,
+ 0xf4f63ee1, 0x43ebffe5, 0x9acdbce8, 0x2dd07dec, 0x77708634, 0xc06d4730,
+ 0x194b043d, 0xae56c539, 0xab068227, 0x1c1b4323, 0xc53d002e, 0x7220c12a,
+ 0xcf9d8e12, 0x78804f16, 0xa1a60c1b, 0x16bbcd1f, 0x13eb8a01, 0xa4f64b05,
+ 0x7dd00808, 0xcacdc90c, 0x07ab9778, 0xb0b6567c, 0x69901571, 0xde8dd475,
+ 0xdbdd936b, 0x6cc0526f, 0xb5e61162, 0x02fbd066, 0xbf469f5e, 0x085b5e5a,
+ 0xd17d1d57, 0x6660dc53, 0x63309b4d, 0xd42d5a49, 0x0d0b1944, 0xba16d840,
+ 0x97c6a5ac, 0x20db64a8, 0xf9fd27a5, 0x4ee0e6a1, 0x4bb0a1bf, 0xfcad60bb,
+ 0x258b23b6, 0x9296e2b2, 0x2f2bad8a, 0x98366c8e, 0x41102f83, 0xf60dee87,
+ 0xf35da999, 0x4440689d, 0x9d662b90, 0x2a7bea94, 0xe71db4e0, 0x500075e4,
+ 0x892636e9, 0x3e3bf7ed, 0x3b6bb0f3, 0x8c7671f7, 0x555032fa, 0xe24df3fe,
+ 0x5ff0bcc6, 0xe8ed7dc2, 0x31cb3ecf, 0x86d6ffcb, 0x8386b8d5, 0x349b79d1,
+ 0xedbd3adc, 0x5aa0fbd8, 0xeee00c69, 0x59fdcd6d, 0x80db8e60, 0x37c64f64,
+ 0x3296087a, 0x858bc97e, 0x5cad8a73, 0xebb04b77, 0x560d044f, 0xe110c54b,
+ 0x38368646, 0x8f2b4742, 0x8a7b005c, 0x3d66c158, 0xe4408255, 0x535d4351,
+ 0x9e3b1d25, 0x2926dc21, 0xf0009f2c, 0x471d5e28, 0x424d1936, 0xf550d832,
+ 0x2c769b3f, 0x9b6b5a3b, 0x26d61503, 0x91cbd407, 0x48ed970a, 0xfff0560e,
+ 0xfaa01110, 0x4dbdd014, 0x949b9319, 0x2386521d, 0x0e562ff1, 0xb94beef5,
+ 0x606dadf8, 0xd7706cfc, 0xd2202be2, 0x653deae6, 0xbc1ba9eb, 0x0b0668ef,
+ 0xb6bb27d7, 0x01a6e6d3, 0xd880a5de, 0x6f9d64da, 0x6acd23c4, 0xddd0e2c0,
+ 0x04f6a1cd, 0xb3eb60c9, 0x7e8d3ebd, 0xc990ffb9, 0x10b6bcb4, 0xa7ab7db0,
+ 0xa2fb3aae, 0x15e6fbaa, 0xccc0b8a7, 0x7bdd79a3, 0xc660369b, 0x717df79f,
+ 0xa85bb492, 0x1f467596, 0x1a163288, 0xad0bf38c, 0x742db081, 0xc3307185,
+ 0x99908a5d, 0x2e8d4b59, 0xf7ab0854, 0x40b6c950, 0x45e68e4e, 0xf2fb4f4a,
+ 0x2bdd0c47, 0x9cc0cd43, 0x217d827b, 0x9660437f, 0x4f460072, 0xf85bc176,
+ 0xfd0b8668, 0x4a16476c, 0x93300461, 0x242dc565, 0xe94b9b11, 0x5e565a15,
+ 0x87701918, 0x306dd81c, 0x353d9f02, 0x82205e06, 0x5b061d0b, 0xec1bdc0f,
+ 0x51a69337, 0xe6bb5233, 0x3f9d113e, 0x8880d03a, 0x8dd09724, 0x3acd5620,
+ 0xe3eb152d, 0x54f6d429, 0x7926a9c5, 0xce3b68c1, 0x171d2bcc, 0xa000eac8,
+ 0xa550add6, 0x124d6cd2, 0xcb6b2fdf, 0x7c76eedb, 0xc1cba1e3, 0x76d660e7,
+ 0xaff023ea, 0x18ede2ee, 0x1dbda5f0, 0xaaa064f4, 0x738627f9, 0xc49be6fd,
+ 0x09fdb889, 0xbee0798d, 0x67c63a80, 0xd0dbfb84, 0xd58bbc9a, 0x62967d9e,
+ 0xbbb03e93, 0x0cadff97, 0xb110b0af, 0x060d71ab, 0xdf2b32a6, 0x6836f3a2,
+ 0x6d66b4bc, 0xda7b75b8, 0x035d36b5, 0xb440f7b1};
+ for (; len; ++data, --len)
+ crc = (crc >> 8) ^ table[(crc & 0xFFu) ^ *data];
+ return crc;
+}
+
+// Code and parameters related to the Program Specific Information (PSI)
+// specification.
+namespace psi {
+
+const uint8_t kProgramAssociationTableId = 0x00u;
+const uint8_t kProgramMapTableId = 0x02u;
+
+const uint16_t kFirstProgramNumber = 0x0001u;
+
+const size_t kCrcSize = 4u;
+const size_t kProgramMapTableElementaryStreamEntryBaseSize = 5u;
+const size_t kTableHeaderSize = 3u;
+
+size_t FillInTablePointer(uint8_t* dst, size_t min_size) {
+ size_t i = 1u;
+ if (i < min_size) {
+ std::memset(&dst[i], 0xFF, min_size - i); // Pointer filler bytes
+ i = min_size;
+ }
+ dst[0] = i - 1u; // Pointer field
+ return i;
+}
+
+size_t FillInTableHeaderAndCrc(uint8_t* header_dst,
+ uint8_t* crc_dst,
+ uint8_t table_id) {
+ size_t i;
+ const uint8_t* const header_end = header_dst + kTableHeaderSize;
+ const uint8_t* const crc_end = crc_dst + kCrcSize;
+ const size_t section_length = static_cast<size_t>(crc_end - header_end);
+ DCHECK_LE(section_length, 1021u);
+
+ // Table header.
+ i = 0u;
+ header_dst[i++] = table_id;
+ header_dst[i++] =
+ (0x1u << 7) | // Section syntax indicator (1 for PAT and PMT)
+ (0x0u << 6) | // Private bit (0 for PAT and PMT)
+ (0x3u << 4) | // Reserved bits (both bits on)
+ (0x0u << 2) | // Section length unused bits (both bits off)
+ ((section_length >> 8) & 0x03u); // Section length (10 bits)
+ header_dst[i++] = section_length & 0xFFu; //
+ DCHECK_EQ(kTableHeaderSize, i);
+
+ // CRC.
+ uint32_t crc =
+ crc32(0xFFFFFFFFu, header_dst, static_cast<size_t>(crc_dst - header_dst));
+ i = 0u;
+ // Avoid swapping the crc by reversing write order.
+ crc_dst[i++] = crc & 0xFFu;
+ crc_dst[i++] = (crc >> 8) & 0xFFu;
+ crc_dst[i++] = (crc >> 16) & 0xFFu;
+ crc_dst[i++] = (crc >> 24) & 0xFFu;
+ DCHECK_EQ(kCrcSize, i);
+ return i;
+}
+
+size_t FillInTableSyntax(uint8_t* dst,
+ uint16_t table_id_extension,
+ uint8_t version_number) {
+ size_t i = 0u;
+ dst[i++] = table_id_extension >> 8;
+ dst[i++] = table_id_extension & 0xFFu;
+ dst[i++] = (0x3u << 6) | // Reserved bits (both bits on)
+ ((version_number & 0x1Fu) << 1) | // Version number (5 bits)
+ (0x1u << 0); // Current indicator
+ dst[i++] = 0u; // Section number
+ dst[i++] = 0u; // Last section number
+ return i;
+}
+
+size_t FillInProgramAssociationTableEntry(uint8_t* dst,
+ uint16_t program_number,
+ unsigned pmt_packet_id) {
+ size_t i = 0u;
+ dst[i++] = program_number >> 8;
+ dst[i++] = program_number & 0xFFu;
+ dst[i++] = (0x7u << 5) | // Reserved bits (all 3 bits on)
+ ((pmt_packet_id >> 8) & 0x1Fu); // Packet identifier (13 bits)
+ dst[i++] = pmt_packet_id & 0xFFu; //
+ return i;
+}
+
+size_t FillInProgramMapTableData(uint8_t* dst, unsigned pcr_packet_id) {
+ size_t i = 0u;
+ dst[i++] = (0x7u << 5) | // Reserved bits (all 3 bits on)
+ ((pcr_packet_id >> 8) & 0x1Fu); // Packet identifier (13 bits)
+ dst[i++] = pcr_packet_id & 0xFFu; //
+ dst[i++] = (0xFu << 4) | // Reserved bits (all 4 bits on)
+ (0x0u << 2) | // Program info length unused bits (both bits off)
+ ((0u >> 8) & 0x3u); // Program info length (10 bits)
+ dst[i++] = 0u & 0xFFu; //
+ // No program descriptors
+ return i;
+}
+
+size_t CalculateElementaryStreamInfoLength(
+ const std::vector<WiFiDisplayElementaryStreamDescriptor>& es_descriptors) {
+ size_t es_info_length = 0u;
+ for (const auto& es_descriptor : es_descriptors)
+ es_info_length += es_descriptor.size();
+ DCHECK_EQ(0u, es_info_length >> 8);
+ return es_info_length;
+}
+
+size_t FillInProgramMapTableElementaryStreamEntry(
+ uint8_t* dst,
+ uint8_t stream_type,
+ unsigned es_packet_id,
+ size_t es_info_length,
+ const std::vector<WiFiDisplayElementaryStreamDescriptor>& es_descriptors) {
+ DCHECK_EQ(CalculateElementaryStreamInfoLength(es_descriptors),
+ es_info_length);
+ DCHECK_EQ(0u, es_info_length >> 10);
+ size_t i = 0u;
+ dst[i++] = stream_type;
+ dst[i++] = (0x7u << 5) | // Reserved bits (all 3 bits on)
+ ((es_packet_id >> 8) & 0x1Fu); // Packet identifier (13 bits)
+ dst[i++] = es_packet_id & 0xFFu; //
+ dst[i++] = (0xFu << 4) | // Reserved bits (all 4 bits on)
+ (0x0u << 2) | // ES info length unused bits (both bits off)
+ ((es_info_length >> 8) & 0x3u); // ES info length (10 bits)
+ dst[i++] = es_info_length & 0xFFu; //
+ for (const auto& es_descriptor : es_descriptors) {
+ std::memcpy(&dst[i], es_descriptor.data(), es_descriptor.size());
+ i += es_descriptor.size();
+ }
+ return i;
+}
+
+} // namespace psi
+
+// Code and parameters related to the MPEG Transport Stream (MPEG-TS)
+// specification.
+namespace ts {
+
+const size_t kAdaptationFieldLengthSize = 1u;
+const size_t kAdaptationFieldFlagsSize = 1u;
+const size_t kPacketHeaderSize = 4u;
+const size_t kProgramClockReferenceSize = 6u;
+
+size_t FillInProgramClockReference(uint8_t* dst, const base::TimeTicks& pcr) {
+ // Convert to the number of 27 MHz ticks since some epoch.
+ const uint64_t us =
+ static_cast<uint64_t>((pcr - base::TimeTicks()).InMicroseconds());
+ const uint64_t n = 27u * us;
+ const uint64_t base = n / 300u; // 90 kHz
+ const uint64_t extension = n % 300u;
+
+ size_t i = 0u;
+ dst[i++] = (base >> 25) & 0xFFu; // Base (33 bits)
+ dst[i++] = (base >> 17) & 0xFFu; //
+ dst[i++] = (base >> 9) & 0xFFu; //
+ dst[i++] = (base >> 1) & 0xFFu; //
+ dst[i++] = ((base & 0x01u) << 7) | //
+ (0x3Fu << 1) | // Reserved bits (all 6 bits on)
+ ((extension >> 8) & 0x1u); // Extension (9 bits)
+ dst[i++] = extension & 0xFFu; //
+ DCHECK_EQ(kProgramClockReferenceSize, i);
+ return i;
+}
+
+size_t FillInAdaptationFieldLengthFromSize(uint8_t* dst, size_t size) {
+ size_t i = 0u;
+ dst[i++] = size - 1u;
+ DCHECK_EQ(kAdaptationFieldLengthSize, i);
+ return i;
+}
+
+size_t FillInAdaptationFieldFlags(uint8_t* dst,
+ bool random_access_indicator,
+ const base::TimeTicks& pcr) {
+ size_t i = 0u;
+ dst[i++] = (0x0u << 7) | // Discontinuity indicator
+ (random_access_indicator << 6) | // Random access indicator
+ (0x0u << 5) | // Elementary stream priority indicator
+ ((!pcr.is_null()) << 4) | // PCR flag
+ (0x0u << 3) | // OPCR flag
+ (0x0u << 2) | // Splicing point flag
+ (0x0u << 1) | // Transport private data flag
+ (0x0u << 0); // Adaptation field extension flag
+ DCHECK_EQ(kAdaptationFieldFlagsSize, i);
+ return i;
+}
+
+size_t FillInAdaptationField(uint8_t* dst,
+ bool random_access_indicator,
+ size_t min_size) {
+ // Reserve space for a length.
+ size_t i = kAdaptationFieldLengthSize;
+
+ if (random_access_indicator || i < min_size) {
+ const base::TimeTicks pcr;
+ i += FillInAdaptationFieldFlags(&dst[i], random_access_indicator, pcr);
+
+ if (i < min_size) {
+ std::memset(&dst[i], 0xFF, min_size - i); // Stuffing bytes.
+ i = min_size;
+ }
+ }
+
+ // Fill in a length now that the size is known.
+ FillInAdaptationFieldLengthFromSize(dst, i);
+
+ return i;
+}
+
+size_t FillInPacketHeader(uint8_t* dst,
+ bool payload_unit_start_indicator,
+ unsigned packet_id,
+ bool adaptation_field_flag,
+ unsigned continuity_counter) {
+ size_t i = 0u;
+ dst[i++] = 0x47; // Sync byte ('G')
+ dst[i++] =
+ (0x0u << 7) | // Transport error indicator
+ (payload_unit_start_indicator << 6) | // Payload unit start indicator
+ (0x0 << 5) | // Transport priority
+ ((packet_id >> 8) & 0x1Fu); // Packet identifier (13 bits)
+ dst[i++] = packet_id & 0xFFu; //
+ dst[i++] = (0x0u << 6) | // Scrambling control (0b00 for not)
+ (adaptation_field_flag << 5) | // Adaptation field flag
+ (0x1u << 4) | // Payload flag
+ (continuity_counter & 0xFu); // Continuity counter
+ DCHECK_EQ(kPacketHeaderSize, i);
+ return i;
+}
+
+} // namespace ts
+
+// Code and parameters related to the WiFi Display specification.
+namespace widi {
+
+const size_t kUnitHeaderMaxSize = 4u;
+
+// Maximum interval between meta information which includes:
+// * Program Association Table (PAT)
+// * Program Map Table (PMT)
+// * Program Clock Reference (PCR)
+const int kMaxMillisecondsBetweenMetaInformation = 100u;
+
+const unsigned kProgramAssociationTablePacketId = 0x0000u;
+const unsigned kProgramMapTablePacketId = 0x0100u;
+const unsigned kProgramClockReferencePacketId = 0x1000u;
+const unsigned kVideoStreamPacketId = 0x1011u;
+const unsigned kFirstAudioStreamPacketId = 0x1100u;
+
+size_t FillInUnitHeader(uint8_t* dst,
+ const WiFiDisplayElementaryStreamInfo& stream_info) {
+ size_t i = 0u;
+
+ if (stream_info.type() == WiFiDisplayElementaryStreamInfo::AUDIO_LPCM) {
+ // Convert an LPCM audio stream descriptor to an LPCM unit header.
+ if (const auto* lpcm_descriptor =
+ stream_info.FindDescriptor<
+ WiFiDisplayElementaryStreamDescriptor::LPCMAudioStream>()) {
+ dst[i++] = 0xA0u; // Sub stream ID (0th sub stream)
+ dst[i++] = WiFiDisplayTransportStreamPacketizer::LPCM::kFramesPerUnit;
+ dst[i++] = ((0x00u << 1) | // Reserved (all 7 bits off)
+ (lpcm_descriptor->emphasis_flag() << 0));
+ dst[i++] = ((lpcm_descriptor->bits_per_sample() << 6) |
+ (lpcm_descriptor->sampling_frequency() << 3) |
+ (lpcm_descriptor->number_of_channels() << 0));
+ }
+ }
+
+ DCHECK_LE(i, kUnitHeaderMaxSize);
+ return i;
+}
+
+} // namespace widi
+
+} // namespace
+
+WiFiDisplayTransportStreamPacket::WiFiDisplayTransportStreamPacket(
+ const uint8_t* header_data,
+ size_t header_size)
+ : header_(header_data, header_size),
+ payload_(header_.end(), 0u),
+ filler_(kPacketSize - header_size) {}
+
+WiFiDisplayTransportStreamPacket::WiFiDisplayTransportStreamPacket(
+ const uint8_t* header_data,
+ size_t header_size,
+ const uint8_t* payload_data)
+ : header_(header_data, header_size),
+ payload_(payload_data, kPacketSize - header_size),
+ filler_(0u) {}
+
+struct WiFiDisplayTransportStreamPacketizer::ElementaryStreamState {
+ ElementaryStreamState(WiFiDisplayElementaryStreamInfo info,
+ uint16_t packet_id,
+ uint8_t stream_id)
+ : info(std::move(info)),
+ info_length(
+ psi::CalculateElementaryStreamInfoLength(this->info.descriptors())),
+ packet_id(packet_id),
+ stream_id(stream_id) {}
+
+ WiFiDisplayElementaryStreamInfo info;
+ uint8_t info_length;
+ struct {
+ uint8_t continuity = 0u;
+ } counters;
+ uint16_t packet_id;
+ uint8_t stream_id;
+};
+
+WiFiDisplayTransportStreamPacketizer::WiFiDisplayTransportStreamPacketizer(
+ const base::TimeDelta& delay_for_unit_time_stamps,
+ std::vector<WiFiDisplayElementaryStreamInfo> stream_infos)
+ : delay_for_unit_time_stamps_(delay_for_unit_time_stamps) {
+ std::memset(&counters_, 0x00, sizeof(counters_));
+ if (!stream_infos.empty())
+ CHECK(SetElementaryStreams(std::move(stream_infos)));
+}
+
+WiFiDisplayTransportStreamPacketizer::~WiFiDisplayTransportStreamPacketizer() {}
+
+bool WiFiDisplayTransportStreamPacketizer::EncodeElementaryStreamUnit(
+ unsigned stream_index,
+ const uint8_t* unit_data,
+ size_t unit_size,
+ bool random_access,
+ base::TimeTicks pts,
+ base::TimeTicks dts,
+ bool flush) {
+ DCHECK(CalledOnValidThread());
+ DCHECK_LT(stream_index, stream_states_.size());
+ ElementaryStreamState& stream_state = stream_states_[stream_index];
+
+ if (program_clock_reference_.is_null() ||
+ base::TimeTicks::Now() - program_clock_reference_ >
+ base::TimeDelta::FromMilliseconds(
+ widi::kMaxMillisecondsBetweenMetaInformation) /
+ 2) {
+ if (!EncodeMetaInformation(false))
+ return false;
+ }
+
+ uint8_t unit_header_data[widi::kUnitHeaderMaxSize];
+ const size_t unit_header_size =
+ widi::FillInUnitHeader(unit_header_data, stream_state.info);
+
+ UpdateDelayForUnitTimeStamps(pts, dts);
+ NormalizeUnitTimeStamps(&pts, &dts);
+
+ WiFiDisplayElementaryStreamPacketizer elementary_stream_packetizer;
+ WiFiDisplayElementaryStreamPacket elementary_stream_packet =
+ elementary_stream_packetizer.EncodeElementaryStreamUnit(
+ stream_state.stream_id, unit_header_data, unit_header_size, unit_data,
+ unit_size, pts, dts);
+
+ size_t adaptation_field_min_size = 0u;
+ uint8_t header_data[WiFiDisplayTransportStreamPacket::kPacketSize];
+ bool is_payload_unit_end;
+ bool is_payload_unit_start = true;
+ size_t remaining_unit_size = elementary_stream_packet.unit().size();
+ do {
+ // Fill in headers and an adaptation field:
+ // * Transport stream packet header
+ // * Transport stream adaptation field
+ // (only for the first and/or the last packet):
+ // - for the first packet to hold flags
+ // - for the last packet to hold padding
+ // * PES packet header (only for the first packet):
+ // - PES packet header base
+ // - Optional PES header base
+ // - Optional PES header optional fields:
+ // - Presentation time stamp
+ // - Decoding time stamp
+ bool adaptation_field_flag = false;
+ size_t header_min_size;
+ if (is_payload_unit_start || is_payload_unit_end) {
+ header_min_size = ts::kPacketHeaderSize;
+ if (is_payload_unit_start) {
+ header_min_size += elementary_stream_packet.header().size() +
+ elementary_stream_packet.unit_header().size();
+ }
+ adaptation_field_min_size =
+ std::max(
+ WiFiDisplayTransportStreamPacket::kPacketSize - header_min_size,
+ remaining_unit_size) -
+ remaining_unit_size;
+ adaptation_field_flag = adaptation_field_min_size > 0 ||
+ (is_payload_unit_start && random_access);
+ }
+ size_t i = 0u;
+ i += ts::FillInPacketHeader(&header_data[i], is_payload_unit_start,
+ stream_state.packet_id, adaptation_field_flag,
+ stream_state.counters.continuity++);
+ if (is_payload_unit_start) {
+ size_t adaptation_field_size = adaptation_field_min_size;
+ if (adaptation_field_flag) {
+ adaptation_field_size = ts::FillInAdaptationField(
+ &header_data[i], random_access, adaptation_field_min_size);
+ i += adaptation_field_size;
+ DCHECK_GE(adaptation_field_size, adaptation_field_min_size);
+ }
+ std::memcpy(&header_data[i], elementary_stream_packet.header().data(),
+ elementary_stream_packet.header().size());
+ i += elementary_stream_packet.header().size();
+ std::memcpy(&header_data[i],
+ elementary_stream_packet.unit_header().data(),
+ elementary_stream_packet.unit_header().size());
+ i += elementary_stream_packet.unit_header().size();
+ DCHECK_EQ(header_min_size + adaptation_field_size, i);
+ } else if (is_payload_unit_end) {
+ if (adaptation_field_flag) {
+ // Fill in an adaptation field only for padding.
+ i += ts::FillInAdaptationField(&header_data[i], false,
+ adaptation_field_min_size);
+ }
+ DCHECK_EQ(header_min_size + adaptation_field_min_size, i);
+ }
+
+ // Delegate the packet.
+ WiFiDisplayTransportStreamPacket packet(
+ header_data, i,
+ elementary_stream_packet.unit().end() - remaining_unit_size);
+ DCHECK_LE(packet.payload().size(), remaining_unit_size);
+ remaining_unit_size -= packet.payload().size();
+ if (!OnPacketizedTransportStreamPacket(
+ packet, flush && remaining_unit_size == 0u)) {
+ return false;
+ }
+
+ // Prepare for the next packet.
+ is_payload_unit_end =
+ remaining_unit_size <=
+ WiFiDisplayTransportStreamPacket::kPacketSize - ts::kPacketHeaderSize;
+ is_payload_unit_start = false;
+ } while (remaining_unit_size > 0u);
+
+ DCHECK_EQ(0u, remaining_unit_size);
+
+ return true;
+}
+
+bool WiFiDisplayTransportStreamPacketizer::EncodeMetaInformation(bool flush) {
+ DCHECK(CalledOnValidThread());
+
+ return (EncodeProgramAssociationTable(false) &&
+ EncodeProgramMapTables(false) && EncodeProgramClockReference(flush));
+}
+
+bool WiFiDisplayTransportStreamPacketizer::EncodeProgramAssociationTable(
+ bool flush) {
+ DCHECK(CalledOnValidThread());
+
+ const uint16_t transport_stream_id = 0x0001u;
+
+ uint8_t header_data[WiFiDisplayTransportStreamPacket::kPacketSize];
+ size_t i = 0u;
+
+ // Fill in a packet header.
+ i += ts::FillInPacketHeader(&header_data[i], true,
+ widi::kProgramAssociationTablePacketId, false,
+ counters_.program_association_table_continuity++);
+
+ // Fill in a minimal table pointer.
+ i += psi::FillInTablePointer(&header_data[i], 0u);
+
+ // Reserve space for a table header.
+ const size_t table_header_index = i;
+ i += psi::kTableHeaderSize;
+
+ // Fill in a table syntax.
+ const uint8_t version_number = 0u;
+ i += psi::FillInTableSyntax(&header_data[i], transport_stream_id,
+ version_number);
+
+ // Fill in program association table data.
+ i += psi::FillInProgramAssociationTableEntry(&header_data[i],
+ psi::kFirstProgramNumber,
+ widi::kProgramMapTablePacketId);
+
+ // Fill in a table header and a CRC now that the table size is known.
+ i += psi::FillInTableHeaderAndCrc(&header_data[table_header_index],
+ &header_data[i],
+ psi::kProgramAssociationTableId);
+
+ // Delegate the packet.
+ return OnPacketizedTransportStreamPacket(
+ WiFiDisplayTransportStreamPacket(header_data, i), flush);
+}
+
+bool WiFiDisplayTransportStreamPacketizer::EncodeProgramClockReference(
+ bool flush) {
+ DCHECK(CalledOnValidThread());
+
+ program_clock_reference_ = base::TimeTicks::Now();
+
+ uint8_t header_data[ts::kPacketHeaderSize + ts::kAdaptationFieldLengthSize +
+ ts::kAdaptationFieldFlagsSize +
+ ts::kProgramClockReferenceSize];
+ size_t i = 0u;
+
+ // Fill in a packet header.
+ i += ts::FillInPacketHeader(&header_data[i], true,
+ widi::kProgramClockReferencePacketId, true,
+ counters_.program_clock_reference_continuity++);
+
+ // Fill in an adaptation field.
+ i += ts::FillInAdaptationFieldLengthFromSize(
+ &header_data[i], WiFiDisplayTransportStreamPacket::kPacketSize - i);
+ i += ts::FillInAdaptationFieldFlags(&header_data[i], false,
+ program_clock_reference_);
+ i += ts::FillInProgramClockReference(&header_data[i],
+ program_clock_reference_);
+
+ DCHECK_EQ(std::end(header_data), header_data + i);
+
+ // Delegate the packet.
+ return OnPacketizedTransportStreamPacket(
+ WiFiDisplayTransportStreamPacket(header_data, i), flush);
+}
+
+bool WiFiDisplayTransportStreamPacketizer::EncodeProgramMapTables(bool flush) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(!stream_states_.empty());
+
+ const uint16_t program_number = psi::kFirstProgramNumber;
+
+ uint8_t header_data[WiFiDisplayTransportStreamPacket::kPacketSize];
+ size_t i = 0u;
+
+ // Fill in a packet header.
+ i += ts::FillInPacketHeader(&header_data[i], true,
+ widi::kProgramMapTablePacketId, false,
+ counters_.program_map_table_continuity++);
+
+ // Fill in a minimal table pointer.
+ i += psi::FillInTablePointer(&header_data[i], 0u);
+
+ // Reserve space for a table header.
+ const size_t table_header_index = i;
+ i += psi::kTableHeaderSize;
+
+ // Fill in a table syntax.
+ i += psi::FillInTableSyntax(&header_data[i], program_number,
+ counters_.program_map_table_version);
+
+ // Fill in program map table data.
+ i += psi::FillInProgramMapTableData(&header_data[i],
+ widi::kProgramClockReferencePacketId);
+ for (const auto& stream_state : stream_states_) {
+ DCHECK_LE(i + psi::kProgramMapTableElementaryStreamEntryBaseSize +
+ stream_state.info_length + psi::kCrcSize,
+ WiFiDisplayTransportStreamPacket::kPacketSize);
+ i += psi::FillInProgramMapTableElementaryStreamEntry(
+ &header_data[i], stream_state.info.type(), stream_state.packet_id,
+ stream_state.info_length, stream_state.info.descriptors());
+ }
+
+ // Fill in a table header and a CRC now that the table size is known.
+ i += psi::FillInTableHeaderAndCrc(&header_data[table_header_index],
+ &header_data[i], psi::kProgramMapTableId);
+
+ // Delegate the packet.
+ return OnPacketizedTransportStreamPacket(
+ WiFiDisplayTransportStreamPacket(header_data, i), flush);
+}
+
+void WiFiDisplayTransportStreamPacketizer::NormalizeUnitTimeStamps(
+ base::TimeTicks* pts,
+ base::TimeTicks* dts) const {
+ DCHECK(CalledOnValidThread());
+
+ // Normalize a presentation time stamp.
+ if (!pts || pts->is_null())
+ return;
+ *pts += delay_for_unit_time_stamps_;
+ DCHECK_LE(program_clock_reference_, *pts);
+
+ // Normalize a decoding time stamp.
+ if (!dts || dts->is_null())
+ return;
+ *dts += delay_for_unit_time_stamps_;
+ DCHECK_LE(program_clock_reference_, *dts);
+ DCHECK_LE(*dts, *pts);
+}
+
+bool WiFiDisplayTransportStreamPacketizer::SetElementaryStreams(
+ std::vector<WiFiDisplayElementaryStreamInfo> stream_infos) {
+ DCHECK(CalledOnValidThread());
+
+ std::vector<ElementaryStreamState> new_stream_states;
+ new_stream_states.reserve(stream_infos.size());
+
+ uint8_t audio_stream_id =
+ WiFiDisplayElementaryStreamPacketizer::kFirstAudioStreamId;
+ uint16_t audio_stream_packet_id = widi::kFirstAudioStreamPacketId;
+ uint8_t private_stream_1_id =
+ WiFiDisplayElementaryStreamPacketizer::kPrivateStream1Id;
+ uint16_t video_stream_packet_id = widi::kVideoStreamPacketId;
+
+ for (auto& stream_info : stream_infos) {
+ uint16_t packet_id;
+ uint8_t stream_id;
+
+ switch (stream_info.type()) {
+ case AUDIO_AAC:
+ packet_id = audio_stream_packet_id++;
+ stream_id = audio_stream_id++;
+ break;
+ case AUDIO_AC3:
+ case AUDIO_LPCM:
+ if (private_stream_1_id !=
+ WiFiDisplayElementaryStreamPacketizer::kPrivateStream1Id) {
+ return false;
+ }
+ packet_id = audio_stream_packet_id++;
+ stream_id = private_stream_1_id++;
+ break;
+ case VIDEO_H264:
+ if (video_stream_packet_id != widi::kVideoStreamPacketId)
+ return false;
+ packet_id = video_stream_packet_id++;
+ stream_id = WiFiDisplayElementaryStreamPacketizer::kFirstVideoStreamId;
+ break;
+ }
+
+ new_stream_states.emplace_back(std::move(stream_info), packet_id,
+ stream_id);
+ }
+
+ // If there are no previous states, there is no previous program map table
+ // to change, either. This ensures that the first encoded program map table
+ // has version 0.
+ if (!stream_states_.empty())
+ ++counters_.program_map_table_version;
+
+ stream_states_.swap(new_stream_states);
+
+ return true;
+}
+
+void WiFiDisplayTransportStreamPacketizer::UpdateDelayForUnitTimeStamps(
+ const base::TimeTicks& pts,
+ const base::TimeTicks& dts) {
+ DCHECK(CalledOnValidThread());
+
+ if (pts.is_null())
+ return;
+
+ const base::TimeTicks now = base::TimeTicks::Now();
+ DCHECK_LE(program_clock_reference_, now);
+
+ // Ensure that delayed time stamps are greater than or equal to now.
+ const base::TimeTicks ts_min =
+ (dts.is_null() ? pts : dts) + delay_for_unit_time_stamps_;
+ if (now > ts_min) {
+ const base::TimeDelta error = now - ts_min;
+ delay_for_unit_time_stamps_ += 2 * error;
+ }
+}
+
+} // namespace extensions
diff --git a/extensions/renderer/api/display_source/wifi_display/wifi_display_transport_stream_packetizer.h b/extensions/renderer/api/display_source/wifi_display/wifi_display_transport_stream_packetizer.h
new file mode 100644
index 0000000..0cdeb6a
--- /dev/null
+++ b/extensions/renderer/api/display_source/wifi_display/wifi_display_transport_stream_packetizer.h
@@ -0,0 +1,176 @@
+// 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_TRANSPORT_STREAM_PACKETIZER_H_
+#define EXTENSIONS_RENDERER_API_DISPLAY_SOURCE_WIFI_DISPLAY_WIFI_DISPLAY_TRANSPORT_STREAM_PACKETIZER_H_
+
+#include <vector>
+
+#include "base/threading/non_thread_safe.h"
+#include "base/time/time.h"
+#include "extensions/renderer/api/display_source/wifi_display/wifi_display_stream_packet_part.h"
+
+namespace extensions {
+
+class WiFiDisplayElementaryStreamInfo;
+class WiFiDisplayElementaryStreamPacket;
+
+// This class represents an MPEG Transport Stream (MPEG-TS) packet containing
+// WiFi Display elementary stream unit data or related meta information.
+class WiFiDisplayTransportStreamPacket {
+ public:
+ enum { kPacketSize = 188u };
+
+ using Part = WiFiDisplayStreamPacketPart;
+
+ // This class represents a possibly empty padding part in the end of
+ // a transport stream packet. The padding part consists of repeated bytes
+ // having the same value.
+ class PaddingPart {
+ public:
+ explicit PaddingPart(unsigned size) : size_(size) {}
+
+ unsigned size() const { return size_; }
+ uint8_t value() const { return 0xFFu; }
+
+ private:
+ const unsigned size_;
+
+ DISALLOW_COPY_AND_ASSIGN(PaddingPart);
+ };
+
+ WiFiDisplayTransportStreamPacket(const uint8_t* header_data,
+ size_t header_size);
+ WiFiDisplayTransportStreamPacket(const uint8_t* header_data,
+ size_t header_size,
+ const uint8_t* payload_data);
+
+ const Part& header() const { return header_; }
+ const Part& payload() const { return payload_; }
+ const PaddingPart& filler() const { return filler_; }
+
+ private:
+ const Part header_;
+ const Part payload_;
+ const PaddingPart filler_;
+
+ DISALLOW_COPY_AND_ASSIGN(WiFiDisplayTransportStreamPacket);
+};
+
+// The WiFi Display transport stream packetizer packetizes unit buffers to
+// MPEG Transport Stream (MPEG-TS) packets containing either meta information
+// or Packetized Elementary Stream (PES) packets containing unit data.
+//
+// Whenever a Transport Stream (TS) packet is fully created and thus ready for
+// further processing, a pure virtual member function
+// |OnPacketizedTransportStreamPacket| is called.
+class WiFiDisplayTransportStreamPacketizer : public base::NonThreadSafe {
+ public:
+ enum ElementaryStreamType : uint8_t {
+ AUDIO_AAC = 0x0Fu,
+ AUDIO_AC3 = 0x81u,
+ AUDIO_LPCM = 0x83u,
+ VIDEO_H264 = 0x1Bu,
+ };
+
+ // Fixed coding parameters for Linear Pulse-Code Modulation (LPCM) audio
+ // streams. See |WiFiDisplayElementaryStreamDescriptor::LPCMAudioStream| for
+ // variable ones.
+ struct LPCM {
+ enum {
+ kFramesPerUnit = 6u,
+ kChannelSamplesPerFrame = 80u,
+ kChannelSamplesPerUnit = kChannelSamplesPerFrame * kFramesPerUnit
+ };
+ };
+
+ WiFiDisplayTransportStreamPacketizer(
+ const base::TimeDelta& delay_for_unit_time_stamps,
+ std::vector<WiFiDisplayElementaryStreamInfo> stream_infos);
+ virtual ~WiFiDisplayTransportStreamPacketizer();
+
+ // Encodes one elementary stream unit buffer (such as one video frame or
+ // 2 * |LPCM::kChannelSamplesPerUnit| two-channel LPCM audio samples) into
+ // packets:
+ // 1) Encodes meta information into meta information packets (by calling
+ // |EncodeMetaInformation|) if needed.
+ // 2) Normalizes unit time stamps (|pts| and |dts|) so that they are never
+ // smaller than a program clock reference.
+ // 3) Encodes the elementary stream unit buffer to unit data packets.
+ // Returns false in the case of an error in which case the caller should stop
+ // encoding.
+ //
+ // In order to minimize encoding delays, |flush| should be true unless
+ // the caller is about to continue encoding immediately.
+ //
+ // Precondition: Elementary streams are configured either using a constructor
+ // or using the |SetElementaryStreams| member function.
+ bool EncodeElementaryStreamUnit(unsigned stream_index,
+ const uint8_t* unit_data,
+ size_t unit_size,
+ bool random_access,
+ base::TimeTicks pts,
+ base::TimeTicks dts,
+ bool flush);
+
+ // Encodes meta information (program association table, program map table and
+ // program clock reference). Returns false in the case of an error in which
+ // case the caller should stop encoding.
+ //
+ // The |EncodeElementaryStreamUnit| member function calls this member function
+ // when needed, thus the caller is responsible for calling this member
+ // function explicitly only if the caller does silence suppression and does
+ // thus not encode all elementary stream units by calling
+ // the |EncodeElementaryStreamUnit| member function.
+ //
+ // In order to minimize encoding delays, |flush| should be true unless
+ // the caller is about to continue encoding immediately.
+ //
+ // Precondition: Elementary streams are configured either using a constructor
+ // or using the |SetElementaryStreams| member function.
+ bool EncodeMetaInformation(bool flush);
+
+ bool SetElementaryStreams(
+ std::vector<WiFiDisplayElementaryStreamInfo> stream_infos);
+
+ void DetachFromThread() { base::NonThreadSafe::DetachFromThread(); }
+
+ protected:
+ bool EncodeProgramAssociationTable(bool flush);
+ bool EncodeProgramClockReference(bool flush);
+ bool EncodeProgramMapTables(bool flush);
+
+ // Normalizes unit time stamps by delaying them in order to ensure that unit
+ // time stamps are never smaller than a program clock reference.
+ // Precondition: The |UpdateDelayForUnitTimeStamps| member function is called.
+ void NormalizeUnitTimeStamps(base::TimeTicks* pts,
+ base::TimeTicks* dts) const;
+ // Update unit time stamp delay in order to ensure that normalized unit time
+ // stamps are never smaller than a program clock reference.
+ void UpdateDelayForUnitTimeStamps(const base::TimeTicks& pts,
+ const base::TimeTicks& dts);
+
+ // Called whenever a Transport Stream (TS) packet is fully created and thus
+ // ready for further processing.
+ virtual bool OnPacketizedTransportStreamPacket(
+ const WiFiDisplayTransportStreamPacket& transport_stream_packet,
+ bool flush) = 0;
+
+ private:
+ struct ElementaryStreamState;
+
+ struct {
+ uint8_t program_association_table_continuity;
+ uint8_t program_map_table_continuity;
+ uint8_t program_map_table_version;
+ uint8_t program_clock_reference_continuity;
+ } counters_;
+ base::TimeDelta delay_for_unit_time_stamps_;
+ base::TimeTicks program_clock_reference_;
+ std::vector<ElementaryStreamState> stream_states_;
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_RENDERER_API_DISPLAY_SOURCE_WIFI_DISPLAY_WIFI_DISPLAY_TRANSPORT_STREAM_PACKETIZER_H_