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 4ba1969bad48618cbf32d3a4fb28d868fb87a6eb..5253a1baa763caf35feef7d1484331d5c144565a 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,16 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
+#include <algorithm> |
+#include <array> |
#include <list> |
#include "base/big_endian.h" |
+#include "base/bind.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_media_packetizer.h" |
#include "extensions/renderer/api/display_source/wifi_display/wifi_display_transport_stream_packetizer.h" |
#include "testing/gtest/include/gtest/gtest.h" |
@@ -42,6 +46,17 @@ const unsigned kPtsFlag = 0x0080u; |
const size_t kUnitDataAlignment = sizeof(uint32_t); |
} |
+namespace rtp { |
+const unsigned kVersionMask = 0xC000u; |
+const unsigned kVersion2 = 0x8000u; |
+const unsigned kPaddingFlag = 0x2000u; |
+const unsigned kExtensionFlag = 0x1000u; |
+const unsigned kContributingSourceCountMask = 0x0F00u; |
+const unsigned kMarkerFlag = 0x0010u; |
+const unsigned kPayloadTypeMask = 0x007Fu; |
+const unsigned kPayloadTypeMP2T = 0x0021u; |
+} // namespace rtp |
+ |
namespace ts { |
const uint64_t kTimeStampMask = (static_cast<uint64_t>(1u) << 33) - 1u; |
const uint64_t kTimeStampSecond = 90000u; // 90 kHz |
@@ -70,6 +85,7 @@ const unsigned kProgramMapTablePacketId = 0x0100u; |
const unsigned kProgramClockReferencePacketId = 0x1000u; |
const unsigned kVideoStreamPacketId = 0x1011u; |
const unsigned kFirstAudioStreamPacketId = 0x1100u; |
+const size_t kMaxTransportStreamPacketCountPerDatagramPacket = 7u; |
} // namespace widi |
template <typename PacketContainer> |
@@ -85,6 +101,36 @@ class PacketCollector { |
PacketContainer packets_; |
}; |
+class FakeMediaPacketizer |
+ : public WiFiDisplayMediaPacketizer, |
+ public PacketCollector<std::vector<std::vector<uint8_t>>> { |
+ public: |
+ FakeMediaPacketizer(const base::TimeDelta& delay_for_unit_time_stamps, |
+ std::vector<WiFiDisplayElementaryStreamInfo> stream_infos) |
+ : WiFiDisplayMediaPacketizer( |
+ delay_for_unit_time_stamps, |
+ std::move(stream_infos), |
+ base::Bind(&FakeMediaPacketizer::OnPacketizedMediaDatagramPacket, |
+ base::Unretained(this))) {} |
+ |
+ // Extend the interface in order to allow to bypass packetization of units to |
+ // Packetized Elementary Stream (PES) packets and further to Transport Stream |
+ // (TS) packets and to test only packetization of TS packets to media |
+ // datagram packets. |
+ bool EncodeTransportStreamPacket( |
+ const WiFiDisplayTransportStreamPacket& transport_stream_packet, |
+ bool flush) { |
+ return OnPacketizedTransportStreamPacket(transport_stream_packet, flush); |
+ } |
+ |
+ private: |
+ bool OnPacketizedMediaDatagramPacket( |
+ WiFiDisplayMediaDatagramPacket media_datagram_packet) { |
+ packets_.emplace_back(std::move(media_datagram_packet)); |
+ return true; |
+ } |
+}; |
+ |
class FakeTransportStreamPacketizer |
: public WiFiDisplayTransportStreamPacketizer, |
public PacketCollector<std::list<WiFiDisplayTransportStreamPacket>> { |
@@ -710,5 +756,150 @@ INSTANTIATE_TEST_CASE_P( |
base::TimeDelta::FromMicroseconds( |
1000 * INT64_C(0x123456789) / 90)))); |
+TEST(WiFiDisplayTransportStreamPacketizationTest, EncodeToMediaDatagramPacket) { |
+ const size_t kPacketHeaderSize = 12u; |
+ |
+ // Create fake units. |
+ const size_t kUnitCount = 12u; |
+ const size_t kUnitSize = |
+ WiFiDisplayTransportStreamPacket::kPacketSize - 4u - 12u; |
+ std::vector<std::array<uint8_t, kUnitSize>> units(kUnitCount); |
+ for (auto& unit : units) |
+ unit.fill(static_cast<uint8_t>(&unit - units.data())); |
+ |
+ // Create transport stream packets. |
+ std::vector<WiFiDisplayElementaryStreamInfo> stream_infos; |
+ stream_infos.emplace_back(WiFiDisplayElementaryStreamInfo::VIDEO_H264); |
+ FakeTransportStreamPacketizer transport_stream_packetizer( |
+ base::TimeDelta::FromMilliseconds(0), std::move(stream_infos)); |
+ for (const auto& unit : units) { |
+ EXPECT_TRUE(transport_stream_packetizer.EncodeElementaryStreamUnit( |
+ 0u, unit.data(), unit.size(), false, base::TimeTicks(), |
+ base::TimeTicks(), &unit == &units.back())); |
+ } |
+ auto transport_stream_packets = transport_stream_packetizer.FetchPackets(); |
+ // There should be exactly one transport stream payload packet for each unit. |
+ // There should also be some but not too many transport stream meta |
+ // information packets. |
+ EXPECT_EQ(1u, transport_stream_packets.size() / kUnitCount); |
+ |
+ // Encode transport stream packets to datagram packets. |
+ FakeMediaPacketizer packetizer( |
+ base::TimeDelta::FromMilliseconds(0), |
+ std::vector<WiFiDisplayElementaryStreamInfo>()); |
+ for (const auto& transport_stream_packet : transport_stream_packets) { |
+ EXPECT_TRUE(packetizer.EncodeTransportStreamPacket( |
+ transport_stream_packet, |
+ &transport_stream_packet == &transport_stream_packets.back())); |
+ } |
+ auto packets = packetizer.FetchPackets(); |
+ |
+ // Check datagram packets. |
+ ProgramClockReference pcr = {ProgramClockReference::kInvalidBase, 0u}; |
+ uint16_t sequence_number; |
+ uint32_t synchronization_source_identifier; |
+ auto transport_stream_packet_it = transport_stream_packets.cbegin(); |
+ for (const auto& packet : packets) { |
+ base::BigEndianReader header_reader( |
+ reinterpret_cast<const char*>(packet.data()), |
+ std::min(kPacketHeaderSize, packet.size())); |
+ |
+ // Packet flags. |
+ uint16_t parsed_u16; |
+ EXPECT_TRUE(header_reader.ReadU16(&parsed_u16)); |
+ EXPECT_EQ(rtp::kVersion2, parsed_u16 & rtp::kVersionMask); |
+ EXPECT_FALSE(parsed_u16 & rtp::kPaddingFlag); |
+ EXPECT_FALSE(parsed_u16 & rtp::kExtensionFlag); |
+ EXPECT_EQ(0u, parsed_u16 & rtp::kContributingSourceCountMask); |
+ EXPECT_FALSE(parsed_u16 & rtp::kMarkerFlag); |
+ EXPECT_EQ(rtp::kPayloadTypeMP2T, parsed_u16 & rtp::kPayloadTypeMask); |
+ |
+ // Packet sequence number. |
+ uint16_t parsed_sequence_number; |
+ EXPECT_TRUE(header_reader.ReadU16(&parsed_sequence_number)); |
+ if (&packet == &packets.front()) |
+ sequence_number = parsed_sequence_number; |
+ EXPECT_EQ(sequence_number++, parsed_sequence_number); |
+ |
+ // Packet time stamp. |
+ uint32_t parsed_time_stamp; |
+ EXPECT_TRUE(header_reader.ReadU32(&parsed_time_stamp)); |
+ if (pcr.base == ProgramClockReference::kInvalidBase) { |
+ // This happens only for the first datagram packet. |
+ EXPECT_TRUE(&packet == &packets.front()); |
+ // Ensure that the next datagram packet reaches the else branch. |
+ EXPECT_FALSE(&packet == &packets.back()); |
+ } else { |
+ // Check that |
+ // 0 <= parsed_time_stamp - pcr.base <= kTimeStampSecond |
+ // but allow pcr.base and parsed_time_stamp to wrap around in 32 bits. |
+ EXPECT_LE((parsed_time_stamp - pcr.base) & 0xFFFFFFFFu, |
+ ts::kTimeStampSecond) |
+ << " Time stamp must not be smaller than PCR!"; |
+ } |
+ |
+ // Packet synchronization source identifier. |
+ uint32_t parsed_synchronization_source_identifier; |
+ EXPECT_TRUE( |
+ header_reader.ReadU32(&parsed_synchronization_source_identifier)); |
+ if (&packet == &packets.front()) { |
+ synchronization_source_identifier = |
+ parsed_synchronization_source_identifier; |
+ } |
+ EXPECT_EQ(synchronization_source_identifier, |
+ parsed_synchronization_source_identifier); |
+ |
+ EXPECT_EQ(0, header_reader.remaining()); |
+ |
+ // Packet payload. |
+ size_t offset = kPacketHeaderSize; |
+ while (offset + WiFiDisplayTransportStreamPacket::kPacketSize <= |
+ packet.size() && |
+ transport_stream_packet_it != transport_stream_packets.end()) { |
+ const auto& transport_stream_packet = *transport_stream_packet_it++; |
+ const PacketPart parsed_transport_stream_packet_header( |
+ packet.data() + offset, transport_stream_packet.header().size()); |
+ const PacketPart parsed_transport_stream_packet_payload( |
+ parsed_transport_stream_packet_header.end(), |
+ transport_stream_packet.payload().size()); |
+ const PacketPart parsed_transport_stream_packet_filler( |
+ parsed_transport_stream_packet_payload.end(), |
+ transport_stream_packet.filler().size()); |
+ offset += WiFiDisplayTransportStreamPacket::kPacketSize; |
+ |
+ // Check bytes. |
+ EXPECT_EQ(transport_stream_packet.header(), |
+ parsed_transport_stream_packet_header); |
+ EXPECT_EQ(transport_stream_packet.payload(), |
+ parsed_transport_stream_packet_payload); |
+ EXPECT_EQ(transport_stream_packet.filler().size(), |
+ std::count(parsed_transport_stream_packet_filler.begin(), |
+ parsed_transport_stream_packet_filler.end(), |
+ transport_stream_packet.filler().value())); |
+ |
+ if (ParseTransportStreamPacketId(transport_stream_packet) == |
+ widi::kProgramClockReferencePacketId) { |
+ pcr = ParseProgramClockReference( |
+ &transport_stream_packet.header().begin()[6]); |
+ } |
+ } |
+ EXPECT_EQ(offset, packet.size()) << " Extra packet payload bytes."; |
+ |
+ // Check that the payload contains a correct number of transport stream |
+ // packets. |
+ const size_t transport_stream_packet_count_in_datagram_packet = |
+ packet.size() / WiFiDisplayTransportStreamPacket::kPacketSize; |
+ if (&packet == &packets.back()) { |
+ EXPECT_GE(transport_stream_packet_count_in_datagram_packet, 1u); |
+ EXPECT_LE(transport_stream_packet_count_in_datagram_packet, |
+ widi::kMaxTransportStreamPacketCountPerDatagramPacket); |
+ } else { |
+ EXPECT_EQ(widi::kMaxTransportStreamPacketCountPerDatagramPacket, |
+ transport_stream_packet_count_in_datagram_packet); |
+ } |
+ } |
+ EXPECT_EQ(transport_stream_packets.end(), transport_stream_packet_it); |
+} |
+ |
} // namespace |
} // namespace extensions |