summaryrefslogtreecommitdiffstats
path: root/extensions
diff options
context:
space:
mode:
authoreero.hakkinen <eero.hakkinen@intel.com>2016-03-23 04:19:02 -0700
committerCommit bot <commit-bot@chromium.org>2016-03-23 11:20:08 +0000
commit208650d9e239e6d8119ba9fb75234c168df177d6 (patch)
treede23b899b977f6169c9c62629747f87ce288a156 /extensions
parent28a88d7f04bb212108adb63002666ead2606bead (diff)
downloadchromium_src-208650d9e239e6d8119ba9fb75234c168df177d6.zip
chromium_src-208650d9e239e6d8119ba9fb75234c168df177d6.tar.gz
chromium_src-208650d9e239e6d8119ba9fb75234c168df177d6.tar.bz2
[chrome.displaySource] Implement media packetizer.
The WiFi Display media packetizer extends a WiFi Display transport stream packetizer and packetizes Transport Stream (TS) packets further to media datagram (RTP) packets which can be passed to a remote sink over an UDP connection. 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/ WiFi Display transport stream packetizer * https://codereview.chromium.org/1796073003/ <-- this CL WiFi Display media packetizer BUG=242107 Review URL: https://codereview.chromium.org/1796073003 Cr-Commit-Position: refs/heads/master@{#382829}
Diffstat (limited to 'extensions')
-rw-r--r--extensions/extensions.gypi2
-rw-r--r--extensions/renderer/api/display_source/wifi_display/wifi_display_media_packetizer.cc110
-rw-r--r--extensions/renderer/api/display_source/wifi_display/wifi_display_media_packetizer.h61
-rw-r--r--extensions/renderer/api/display_source/wifi_display/wifi_display_media_packetizer_unittest.cc191
4 files changed, 364 insertions, 0 deletions
diff --git a/extensions/extensions.gypi b/extensions/extensions.gypi
index 4ecff0d..7af2233 100644
--- a/extensions/extensions.gypi
+++ b/extensions/extensions.gypi
@@ -1044,6 +1044,8 @@
'renderer/api/display_source/wifi_display/wifi_display_elementary_stream_packetizer.h',
'renderer/api/display_source/wifi_display/wifi_display_media_manager.cc',
'renderer/api/display_source/wifi_display/wifi_display_media_manager.h',
+ 'renderer/api/display_source/wifi_display/wifi_display_media_packetizer.cc',
+ 'renderer/api/display_source/wifi_display/wifi_display_media_packetizer.h',
'renderer/api/display_source/wifi_display/wifi_display_session.cc',
'renderer/api/display_source/wifi_display/wifi_display_session.h',
'renderer/api/display_source/wifi_display/wifi_display_stream_packet_part.h',
diff --git a/extensions/renderer/api/display_source/wifi_display/wifi_display_media_packetizer.cc b/extensions/renderer/api/display_source/wifi_display/wifi_display_media_packetizer.cc
new file mode 100644
index 0000000..f8f9cf23
--- /dev/null
+++ b/extensions/renderer/api/display_source/wifi_display/wifi_display_media_packetizer.cc
@@ -0,0 +1,110 @@
+// 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_media_packetizer.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "crypto/random.h"
+#include "extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_info.h"
+
+namespace extensions {
+namespace {
+const size_t kMaxTransportStreamPacketCount = 7u;
+const uint8_t kProtocolPayloadTypeMP2T = 33u;
+const uint8_t kProtocolVersion = 2u;
+} // namespace
+
+WiFiDisplayMediaDatagramPacket::WiFiDisplayMediaDatagramPacket() = default;
+
+WiFiDisplayMediaDatagramPacket::WiFiDisplayMediaDatagramPacket(
+ WiFiDisplayMediaDatagramPacket&&) = default;
+
+WiFiDisplayMediaPacketizer::WiFiDisplayMediaPacketizer(
+ const base::TimeDelta& delay_for_unit_time_stamps,
+ std::vector<WiFiDisplayElementaryStreamInfo> stream_infos,
+ const PacketizedCallback& on_packetized)
+ : WiFiDisplayTransportStreamPacketizer(delay_for_unit_time_stamps,
+ std::move(stream_infos)),
+ on_packetized_media_datagram_packet_(on_packetized) {
+ // Sequence numbers are mainly used for detecting lossed packets within one
+ // RTP session. The initial value SHOULD be random (unpredictable) to make
+ // known-plaintext attacks on encryption more difficult, in case the packets
+ // flow through a translator that encrypts them.
+ crypto::RandBytes(&sequence_number_, sizeof(sequence_number_));
+ base::RandBytes(&synchronization_source_identifier_,
+ sizeof(synchronization_source_identifier_));
+}
+
+WiFiDisplayMediaPacketizer::~WiFiDisplayMediaPacketizer() {}
+
+bool WiFiDisplayMediaPacketizer::OnPacketizedTransportStreamPacket(
+ const WiFiDisplayTransportStreamPacket& transport_stream_packet,
+ bool flush) {
+ DCHECK(CalledOnValidThread());
+
+ if (media_datagram_packet_.empty()) {
+ // Convert time to the number of 90 kHz ticks since some epoch.
+ const uint64_t us = static_cast<uint64_t>(
+ (base::TimeTicks::Now() - base::TimeTicks()).InMicroseconds());
+ const uint64_t time_stamp = (us * 90u + 500u) / 1000u;
+ const uint8_t header_without_identifiers[] = {
+ (kProtocolVersion << 6 | // Version (2 bits)
+ 0x0u << 5 | // Padding (no)
+ 0x0u << 4 | // Extension (no)
+ 0u << 0), // CSRC count (4 bits)
+ (0x0u << 7 | // Marker (no)
+ kProtocolPayloadTypeMP2T << 0), // Payload type (7 bits)
+ sequence_number_ >> 8, // Sequence number (16 bits)
+ sequence_number_ & 0xFFu, //
+ (time_stamp >> 24) & 0xFFu, // Time stamp (32 bits)
+ (time_stamp >> 16) & 0xFFu, //
+ (time_stamp >> 8) & 0xFFu, //
+ time_stamp & 0xFFu}; //
+ ++sequence_number_;
+ media_datagram_packet_.reserve(
+ sizeof(header_without_identifiers) +
+ sizeof(synchronization_source_identifier_) +
+ kMaxTransportStreamPacketCount *
+ WiFiDisplayTransportStreamPacket::kPacketSize);
+ media_datagram_packet_.insert(media_datagram_packet_.end(),
+ std::begin(header_without_identifiers),
+ std::end(header_without_identifiers));
+ media_datagram_packet_.insert(
+ media_datagram_packet_.end(),
+ std::begin(synchronization_source_identifier_),
+ std::end(synchronization_source_identifier_));
+ DCHECK_EQ(0u, media_datagram_packet_.size() /
+ WiFiDisplayTransportStreamPacket::kPacketSize);
+ }
+
+ // Append header and payload data.
+ media_datagram_packet_.insert(media_datagram_packet_.end(),
+ transport_stream_packet.header().begin(),
+ transport_stream_packet.header().end());
+ media_datagram_packet_.insert(media_datagram_packet_.end(),
+ transport_stream_packet.payload().begin(),
+ transport_stream_packet.payload().end());
+ media_datagram_packet_.insert(media_datagram_packet_.end(),
+ transport_stream_packet.filler().size(),
+ transport_stream_packet.filler().value());
+
+ // Combine multiple transport stream packets into one datagram packet
+ // by delaying delegation whenever possible.
+ if (!flush) {
+ const size_t transport_stream_packet_count =
+ media_datagram_packet_.size() /
+ WiFiDisplayTransportStreamPacket::kPacketSize;
+ if (transport_stream_packet_count < kMaxTransportStreamPacketCount)
+ return true;
+ }
+
+ WiFiDisplayMediaDatagramPacket packet;
+ packet.swap(media_datagram_packet_);
+ return on_packetized_media_datagram_packet_.Run(std::move(packet));
+}
+
+} // namespace extensions
diff --git a/extensions/renderer/api/display_source/wifi_display/wifi_display_media_packetizer.h b/extensions/renderer/api/display_source/wifi_display/wifi_display_media_packetizer.h
new file mode 100644
index 0000000..23c07e0
--- /dev/null
+++ b/extensions/renderer/api/display_source/wifi_display/wifi_display_media_packetizer.h
@@ -0,0 +1,61 @@
+// 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_MEDIA_PACKETIZER_H_
+#define EXTENSIONS_RENDERER_API_DISPLAY_SOURCE_WIFI_DISPLAY_WIFI_DISPLAY_MEDIA_PACKETIZER_H_
+
+#include <vector>
+
+#include "base/callback.h"
+#include "base/move.h"
+#include "extensions/renderer/api/display_source/wifi_display/wifi_display_transport_stream_packetizer.h"
+
+namespace extensions {
+
+// This class represents an RTP datagram packet containing MPEG Transport
+// Stream (MPEG-TS) packets as a payload.
+class WiFiDisplayMediaDatagramPacket : public std::vector<uint8_t> {
+ public:
+ WiFiDisplayMediaDatagramPacket();
+ WiFiDisplayMediaDatagramPacket(WiFiDisplayMediaDatagramPacket&&);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN_WITH_MOVE_FOR_BIND(WiFiDisplayMediaDatagramPacket);
+};
+
+// The WiFi Display media packetizer packetizes unit buffers to media datagram
+// packets containing MPEG Transport Stream (MPEG-TS) packets containing either
+// meta information or Packetized Elementary Stream (PES) packets containing
+// unit data.
+//
+// Whenever a media datagram packet is fully created and thus ready for further
+// processing, a callback is called.
+class WiFiDisplayMediaPacketizer : public WiFiDisplayTransportStreamPacketizer {
+ public:
+ using PacketizedCallback =
+ base::Callback<bool(WiFiDisplayMediaDatagramPacket)>;
+
+ WiFiDisplayMediaPacketizer(
+ const base::TimeDelta& delay_for_unit_time_stamps,
+ std::vector<WiFiDisplayElementaryStreamInfo> stream_infos,
+ const PacketizedCallback& on_packetized);
+ ~WiFiDisplayMediaPacketizer() override;
+
+ protected:
+ bool OnPacketizedTransportStreamPacket(
+ const WiFiDisplayTransportStreamPacket& transport_stream_packet,
+ bool flush) override;
+
+ private:
+ using SourceIdentifier = uint8_t[4];
+
+ WiFiDisplayMediaDatagramPacket media_datagram_packet_;
+ PacketizedCallback on_packetized_media_datagram_packet_;
+ uint16_t sequence_number_;
+ SourceIdentifier synchronization_source_identifier_;
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_RENDERER_API_DISPLAY_SOURCE_WIFI_DISPLAY_WIFI_DISPLAY_MEDIA_PACKETIZER_H_
diff --git a/extensions/renderer/api/display_source/wifi_display/wifi_display_media_packetizer_unittest.cc b/extensions/renderer/api/display_source/wifi_display/wifi_display_media_packetizer_unittest.cc
index 4ba1969..5253a1b 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