| Index: extensions/renderer/api/display_source/wifi_display/wifi_display_media_packetizer_unittest.cc
|
| 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 a8b769a4421ace00b07782a61c9e5344a614f764..4ba1969bad48618cbf32d3a4fb28d868fb87a6eb 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,
|
|
|