| /* | 
 |  *  Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. | 
 |  * | 
 |  *  Use of this source code is governed by a BSD-style license | 
 |  *  that can be found in the LICENSE file in the root of the source | 
 |  *  tree. An additional intellectual property rights grant can be found | 
 |  *  in the file PATENTS.  All contributing project authors may | 
 |  *  be found in the AUTHORS file in the root of the source tree. | 
 |  */ | 
 | #include "modules/video_coding/h264_packet_buffer.h" | 
 |  | 
 | #include <cstring> | 
 | #include <limits> | 
 | #include <ostream> | 
 | #include <string> | 
 | #include <utility> | 
 |  | 
 | #include "api/array_view.h" | 
 | #include "api/video/render_resolution.h" | 
 | #include "common_video/h264/h264_common.h" | 
 | #include "rtc_base/system/unused.h" | 
 | #include "test/gmock.h" | 
 | #include "test/gtest.h" | 
 |  | 
 | namespace webrtc { | 
 | namespace { | 
 |  | 
 | using ::testing::ElementsAreArray; | 
 | using ::testing::Eq; | 
 | using ::testing::IsEmpty; | 
 | using ::testing::SizeIs; | 
 |  | 
 | using H264::NaluType::kAud; | 
 | using H264::NaluType::kFuA; | 
 | using H264::NaluType::kIdr; | 
 | using H264::NaluType::kPps; | 
 | using H264::NaluType::kSlice; | 
 | using H264::NaluType::kSps; | 
 | using H264::NaluType::kStapA; | 
 |  | 
 | constexpr int kBufferSize = 2048; | 
 |  | 
 | std::vector<uint8_t> StartCode() { | 
 |   return {0, 0, 0, 1}; | 
 | } | 
 |  | 
 | NaluInfo MakeNaluInfo(uint8_t type) { | 
 |   NaluInfo res; | 
 |   res.type = type; | 
 |   res.sps_id = -1; | 
 |   res.pps_id = -1; | 
 |   return res; | 
 | } | 
 |  | 
 | class Packet { | 
 |  public: | 
 |   explicit Packet(H264PacketizationTypes type); | 
 |  | 
 |   Packet& Idr(std::vector<uint8_t> payload = {9, 9, 9}); | 
 |   Packet& Slice(std::vector<uint8_t> payload = {9, 9, 9}); | 
 |   Packet& Sps(std::vector<uint8_t> payload = {9, 9, 9}); | 
 |   Packet& SpsWithResolution(RenderResolution resolution, | 
 |                             std::vector<uint8_t> payload = {9, 9, 9}); | 
 |   Packet& Pps(std::vector<uint8_t> payload = {9, 9, 9}); | 
 |   Packet& Aud(); | 
 |   Packet& Marker(); | 
 |   Packet& AsFirstFragment(); | 
 |   Packet& Time(uint32_t rtp_timestamp); | 
 |   Packet& SeqNum(uint16_t rtp_seq_num); | 
 |  | 
 |   std::unique_ptr<H264PacketBuffer::Packet> Build(); | 
 |  | 
 |  private: | 
 |   rtc::CopyOnWriteBuffer BuildFuaPayload() const; | 
 |   rtc::CopyOnWriteBuffer BuildSingleNaluPayload() const; | 
 |   rtc::CopyOnWriteBuffer BuildStapAPayload() const; | 
 |  | 
 |   RTPVideoHeaderH264& H264Header() { | 
 |     return absl::get<RTPVideoHeaderH264>(video_header_.video_type_header); | 
 |   } | 
 |   const RTPVideoHeaderH264& H264Header() const { | 
 |     return absl::get<RTPVideoHeaderH264>(video_header_.video_type_header); | 
 |   } | 
 |  | 
 |   H264PacketizationTypes type_; | 
 |   RTPVideoHeader video_header_; | 
 |   bool first_fragment_ = false; | 
 |   bool marker_bit_ = false; | 
 |   uint32_t rtp_timestamp_ = 0; | 
 |   uint16_t rtp_seq_num_ = 0; | 
 |   std::vector<std::vector<uint8_t>> nalu_payloads_; | 
 | }; | 
 |  | 
 | Packet::Packet(H264PacketizationTypes type) : type_(type) { | 
 |   video_header_.video_type_header.emplace<RTPVideoHeaderH264>(); | 
 | } | 
 |  | 
 | Packet& Packet::Idr(std::vector<uint8_t> payload) { | 
 |   auto& h264_header = H264Header(); | 
 |   h264_header.nalus[h264_header.nalus_length++] = MakeNaluInfo(kIdr); | 
 |   nalu_payloads_.push_back(std::move(payload)); | 
 |   return *this; | 
 | } | 
 |  | 
 | Packet& Packet::Slice(std::vector<uint8_t> payload) { | 
 |   auto& h264_header = H264Header(); | 
 |   h264_header.nalus[h264_header.nalus_length++] = MakeNaluInfo(kSlice); | 
 |   nalu_payloads_.push_back(std::move(payload)); | 
 |   return *this; | 
 | } | 
 |  | 
 | Packet& Packet::Sps(std::vector<uint8_t> payload) { | 
 |   auto& h264_header = H264Header(); | 
 |   h264_header.nalus[h264_header.nalus_length++] = MakeNaluInfo(kSps); | 
 |   nalu_payloads_.push_back(std::move(payload)); | 
 |   return *this; | 
 | } | 
 |  | 
 | Packet& Packet::SpsWithResolution(RenderResolution resolution, | 
 |                                   std::vector<uint8_t> payload) { | 
 |   auto& h264_header = H264Header(); | 
 |   h264_header.nalus[h264_header.nalus_length++] = MakeNaluInfo(kSps); | 
 |   video_header_.width = resolution.Width(); | 
 |   video_header_.height = resolution.Height(); | 
 |   nalu_payloads_.push_back(std::move(payload)); | 
 |   return *this; | 
 | } | 
 |  | 
 | Packet& Packet::Pps(std::vector<uint8_t> payload) { | 
 |   auto& h264_header = H264Header(); | 
 |   h264_header.nalus[h264_header.nalus_length++] = MakeNaluInfo(kPps); | 
 |   nalu_payloads_.push_back(std::move(payload)); | 
 |   return *this; | 
 | } | 
 |  | 
 | Packet& Packet::Aud() { | 
 |   auto& h264_header = H264Header(); | 
 |   h264_header.nalus[h264_header.nalus_length++] = MakeNaluInfo(kAud); | 
 |   nalu_payloads_.push_back({}); | 
 |   return *this; | 
 | } | 
 |  | 
 | Packet& Packet::Marker() { | 
 |   marker_bit_ = true; | 
 |   return *this; | 
 | } | 
 |  | 
 | Packet& Packet::AsFirstFragment() { | 
 |   first_fragment_ = true; | 
 |   return *this; | 
 | } | 
 |  | 
 | Packet& Packet::Time(uint32_t rtp_timestamp) { | 
 |   rtp_timestamp_ = rtp_timestamp; | 
 |   return *this; | 
 | } | 
 |  | 
 | Packet& Packet::SeqNum(uint16_t rtp_seq_num) { | 
 |   rtp_seq_num_ = rtp_seq_num; | 
 |   return *this; | 
 | } | 
 |  | 
 | std::unique_ptr<H264PacketBuffer::Packet> Packet::Build() { | 
 |   auto res = std::make_unique<H264PacketBuffer::Packet>(); | 
 |  | 
 |   auto& h264_header = H264Header(); | 
 |   switch (type_) { | 
 |     case kH264FuA: { | 
 |       RTC_CHECK_EQ(h264_header.nalus_length, 1); | 
 |       res->video_payload = BuildFuaPayload(); | 
 |       break; | 
 |     } | 
 |     case kH264SingleNalu: { | 
 |       RTC_CHECK_EQ(h264_header.nalus_length, 1); | 
 |       res->video_payload = BuildSingleNaluPayload(); | 
 |       break; | 
 |     } | 
 |     case kH264StapA: { | 
 |       RTC_CHECK_GT(h264_header.nalus_length, 1); | 
 |       RTC_CHECK_LE(h264_header.nalus_length, kMaxNalusPerPacket); | 
 |       res->video_payload = BuildStapAPayload(); | 
 |       break; | 
 |     } | 
 |   } | 
 |  | 
 |   if (type_ == kH264FuA && !first_fragment_) { | 
 |     h264_header.nalus_length = 0; | 
 |   } | 
 |  | 
 |   h264_header.packetization_type = type_; | 
 |   res->marker_bit = marker_bit_; | 
 |   res->video_header = video_header_; | 
 |   res->timestamp = rtp_timestamp_; | 
 |   res->seq_num = rtp_seq_num_; | 
 |   res->video_header.codec = kVideoCodecH264; | 
 |  | 
 |   return res; | 
 | } | 
 |  | 
 | rtc::CopyOnWriteBuffer Packet::BuildFuaPayload() const { | 
 |   return rtc::CopyOnWriteBuffer(nalu_payloads_[0]); | 
 | } | 
 |  | 
 | rtc::CopyOnWriteBuffer Packet::BuildSingleNaluPayload() const { | 
 |   rtc::CopyOnWriteBuffer res; | 
 |   auto& h264_header = H264Header(); | 
 |   res.AppendData(&h264_header.nalus[0].type, 1); | 
 |   res.AppendData(nalu_payloads_[0]); | 
 |   return res; | 
 | } | 
 |  | 
 | rtc::CopyOnWriteBuffer Packet::BuildStapAPayload() const { | 
 |   rtc::CopyOnWriteBuffer res; | 
 |  | 
 |   const uint8_t indicator = H264::NaluType::kStapA; | 
 |   res.AppendData(&indicator, 1); | 
 |  | 
 |   auto& h264_header = H264Header(); | 
 |   for (size_t i = 0; i < h264_header.nalus_length; ++i) { | 
 |     // The two first bytes indicates the nalu segment size. | 
 |     uint8_t length_as_array[2] = { | 
 |         0, static_cast<uint8_t>(nalu_payloads_[i].size() + 1)}; | 
 |     res.AppendData(length_as_array); | 
 |  | 
 |     res.AppendData(&h264_header.nalus[i].type, 1); | 
 |     res.AppendData(nalu_payloads_[i]); | 
 |   } | 
 |   return res; | 
 | } | 
 |  | 
 | rtc::ArrayView<const uint8_t> PacketPayload( | 
 |     const std::unique_ptr<H264PacketBuffer::Packet>& packet) { | 
 |   return packet->video_payload; | 
 | } | 
 |  | 
 | std::vector<uint8_t> FlatVector( | 
 |     const std::vector<std::vector<uint8_t>>& elems) { | 
 |   std::vector<uint8_t> res; | 
 |   for (const auto& elem : elems) { | 
 |     res.insert(res.end(), elem.begin(), elem.end()); | 
 |   } | 
 |   return res; | 
 | } | 
 |  | 
 | TEST(H264PacketBufferTest, IdrIsKeyframe) { | 
 |   H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/true); | 
 |  | 
 |   EXPECT_THAT( | 
 |       packet_buffer.InsertPacket(Packet(kH264SingleNalu).Idr().Marker().Build()) | 
 |           .packets, | 
 |       SizeIs(1)); | 
 | } | 
 |  | 
 | TEST(H264PacketBufferTest, IdrIsNotKeyframe) { | 
 |   H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); | 
 |  | 
 |   EXPECT_THAT( | 
 |       packet_buffer.InsertPacket(Packet(kH264SingleNalu).Idr().Marker().Build()) | 
 |           .packets, | 
 |       IsEmpty()); | 
 | } | 
 |  | 
 | TEST(H264PacketBufferTest, IdrIsKeyframeFuaRequiresFirstFragmet) { | 
 |   H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/true); | 
 |  | 
 |   // Not marked as the first fragment | 
 |   EXPECT_THAT( | 
 |       packet_buffer | 
 |           .InsertPacket(Packet(kH264FuA).Idr().SeqNum(0).Time(0).Build()) | 
 |           .packets, | 
 |       IsEmpty()); | 
 |  | 
 |   EXPECT_THAT(packet_buffer | 
 |                   .InsertPacket( | 
 |                       Packet(kH264FuA).Idr().SeqNum(1).Time(0).Marker().Build()) | 
 |                   .packets, | 
 |               IsEmpty()); | 
 |  | 
 |   // Marked as first fragment | 
 |   EXPECT_THAT(packet_buffer | 
 |                   .InsertPacket(Packet(kH264FuA) | 
 |                                     .Idr() | 
 |                                     .SeqNum(2) | 
 |                                     .Time(1) | 
 |                                     .AsFirstFragment() | 
 |                                     .Build()) | 
 |                   .packets, | 
 |               IsEmpty()); | 
 |  | 
 |   EXPECT_THAT(packet_buffer | 
 |                   .InsertPacket( | 
 |                       Packet(kH264FuA).Idr().SeqNum(3).Time(1).Marker().Build()) | 
 |                   .packets, | 
 |               SizeIs(2)); | 
 | } | 
 |  | 
 | TEST(H264PacketBufferTest, SpsPpsIdrIsKeyframeSingleNalus) { | 
 |   H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); | 
 |  | 
 |   RTC_UNUSED(packet_buffer.InsertPacket( | 
 |       Packet(kH264SingleNalu).Sps().SeqNum(0).Time(0).Build())); | 
 |   RTC_UNUSED(packet_buffer.InsertPacket( | 
 |       Packet(kH264SingleNalu).Pps().SeqNum(1).Time(0).Build())); | 
 |   EXPECT_THAT( | 
 |       packet_buffer | 
 |           .InsertPacket( | 
 |               Packet(kH264SingleNalu).Idr().SeqNum(2).Time(0).Marker().Build()) | 
 |           .packets, | 
 |       SizeIs(3)); | 
 | } | 
 |  | 
 | TEST(H264PacketBufferTest, PpsIdrIsNotKeyframeSingleNalus) { | 
 |   H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); | 
 |  | 
 |   RTC_UNUSED(packet_buffer.InsertPacket( | 
 |       Packet(kH264SingleNalu).Pps().SeqNum(0).Time(0).Build())); | 
 |   EXPECT_THAT( | 
 |       packet_buffer | 
 |           .InsertPacket( | 
 |               Packet(kH264SingleNalu).Idr().SeqNum(1).Time(0).Marker().Build()) | 
 |           .packets, | 
 |       IsEmpty()); | 
 | } | 
 |  | 
 | TEST(H264PacketBufferTest, SpsIdrIsNotKeyframeSingleNalus) { | 
 |   H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); | 
 |  | 
 |   RTC_UNUSED(packet_buffer.InsertPacket( | 
 |       Packet(kH264SingleNalu).Sps().SeqNum(0).Time(0).Build())); | 
 |   EXPECT_THAT( | 
 |       packet_buffer | 
 |           .InsertPacket( | 
 |               Packet(kH264SingleNalu).Idr().SeqNum(1).Time(0).Marker().Build()) | 
 |           .packets, | 
 |       IsEmpty()); | 
 | } | 
 |  | 
 | TEST(H264PacketBufferTest, SpsPpsIdrIsKeyframeStapA) { | 
 |   H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); | 
 |  | 
 |   EXPECT_THAT(packet_buffer | 
 |                   .InsertPacket(Packet(kH264StapA) | 
 |                                     .Sps() | 
 |                                     .Pps() | 
 |                                     .Idr() | 
 |                                     .SeqNum(0) | 
 |                                     .Time(0) | 
 |                                     .Marker() | 
 |                                     .Build()) | 
 |                   .packets, | 
 |               SizeIs(1)); | 
 | } | 
 |  | 
 | TEST(H264PacketBufferTest, PpsIdrIsNotKeyframeStapA) { | 
 |   H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); | 
 |  | 
 |   EXPECT_THAT( | 
 |       packet_buffer | 
 |           .InsertPacket( | 
 |               Packet(kH264StapA).Pps().Idr().SeqNum(0).Time(0).Marker().Build()) | 
 |           .packets, | 
 |       IsEmpty()); | 
 | } | 
 |  | 
 | TEST(H264PacketBufferTest, SpsIdrIsNotKeyframeStapA) { | 
 |   H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); | 
 |  | 
 |   EXPECT_THAT( | 
 |       packet_buffer | 
 |           .InsertPacket( | 
 |               Packet(kH264StapA).Sps().Idr().SeqNum(2).Time(2).Marker().Build()) | 
 |           .packets, | 
 |       IsEmpty()); | 
 |  | 
 |   EXPECT_THAT(packet_buffer | 
 |                   .InsertPacket(Packet(kH264StapA) | 
 |                                     .Sps() | 
 |                                     .Pps() | 
 |                                     .Idr() | 
 |                                     .SeqNum(3) | 
 |                                     .Time(3) | 
 |                                     .Marker() | 
 |                                     .Build()) | 
 |                   .packets, | 
 |               SizeIs(1)); | 
 | } | 
 |  | 
 | TEST(H264PacketBufferTest, InsertingSpsPpsLastCompletesKeyframe) { | 
 |   H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); | 
 |  | 
 |   RTC_UNUSED(packet_buffer.InsertPacket( | 
 |       Packet(kH264SingleNalu).Idr().SeqNum(2).Time(1).Marker().Build())); | 
 |  | 
 |   EXPECT_THAT(packet_buffer | 
 |                   .InsertPacket( | 
 |                       Packet(kH264StapA).Sps().Pps().SeqNum(1).Time(1).Build()) | 
 |                   .packets, | 
 |               SizeIs(2)); | 
 | } | 
 |  | 
 | TEST(H264PacketBufferTest, InsertingMidFuaCompletesFrame) { | 
 |   H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); | 
 |  | 
 |   EXPECT_THAT(packet_buffer | 
 |                   .InsertPacket(Packet(kH264StapA) | 
 |                                     .Sps() | 
 |                                     .Pps() | 
 |                                     .Idr() | 
 |                                     .SeqNum(0) | 
 |                                     .Time(0) | 
 |                                     .Marker() | 
 |                                     .Build()) | 
 |                   .packets, | 
 |               SizeIs(1)); | 
 |  | 
 |   RTC_UNUSED(packet_buffer.InsertPacket( | 
 |       Packet(kH264FuA).Slice().SeqNum(1).Time(1).AsFirstFragment().Build())); | 
 |   RTC_UNUSED(packet_buffer.InsertPacket( | 
 |       Packet(kH264FuA).Slice().SeqNum(3).Time(1).Marker().Build())); | 
 |   EXPECT_THAT( | 
 |       packet_buffer | 
 |           .InsertPacket(Packet(kH264FuA).Slice().SeqNum(2).Time(1).Build()) | 
 |           .packets, | 
 |       SizeIs(3)); | 
 | } | 
 |  | 
 | TEST(H264PacketBufferTest, SeqNumJumpDoesNotCompleteFrame) { | 
 |   H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); | 
 |  | 
 |   EXPECT_THAT(packet_buffer | 
 |                   .InsertPacket(Packet(kH264StapA) | 
 |                                     .Sps() | 
 |                                     .Pps() | 
 |                                     .Idr() | 
 |                                     .SeqNum(0) | 
 |                                     .Time(0) | 
 |                                     .Marker() | 
 |                                     .Build()) | 
 |                   .packets, | 
 |               SizeIs(1)); | 
 |  | 
 |   EXPECT_THAT( | 
 |       packet_buffer | 
 |           .InsertPacket(Packet(kH264FuA).Slice().SeqNum(1).Time(1).Build()) | 
 |           .packets, | 
 |       IsEmpty()); | 
 |  | 
 |   // Add `kBufferSize` to make the index of the sequence number wrap and end up | 
 |   // where the packet with sequence number 2 would have ended up. | 
 |   EXPECT_THAT(packet_buffer | 
 |                   .InsertPacket(Packet(kH264FuA) | 
 |                                     .Slice() | 
 |                                     .SeqNum(2 + kBufferSize) | 
 |                                     .Time(3) | 
 |                                     .Marker() | 
 |                                     .Build()) | 
 |                   .packets, | 
 |               IsEmpty()); | 
 | } | 
 |  | 
 | TEST(H264PacketBufferTest, OldFramesAreNotCompletedAfterBufferWrap) { | 
 |   H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); | 
 |  | 
 |   EXPECT_THAT(packet_buffer | 
 |                   .InsertPacket(Packet(kH264SingleNalu) | 
 |                                     .Slice() | 
 |                                     .SeqNum(1) | 
 |                                     .Time(1) | 
 |                                     .Marker() | 
 |                                     .Build()) | 
 |                   .packets, | 
 |               IsEmpty()); | 
 |  | 
 |   // New keyframe, preceedes packet with sequence number 1 in the buffer. | 
 |   EXPECT_THAT(packet_buffer | 
 |                   .InsertPacket(Packet(kH264StapA) | 
 |                                     .Sps() | 
 |                                     .Pps() | 
 |                                     .Idr() | 
 |                                     .SeqNum(kBufferSize) | 
 |                                     .Time(kBufferSize) | 
 |                                     .Marker() | 
 |                                     .Build()) | 
 |                   .packets, | 
 |               SizeIs(1)); | 
 | } | 
 |  | 
 | TEST(H264PacketBufferTest, OldPacketsDontBlockNewPackets) { | 
 |   H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); | 
 |   EXPECT_THAT(packet_buffer | 
 |                   .InsertPacket(Packet(kH264StapA) | 
 |                                     .Sps() | 
 |                                     .Pps() | 
 |                                     .Idr() | 
 |                                     .SeqNum(kBufferSize) | 
 |                                     .Time(kBufferSize) | 
 |                                     .Marker() | 
 |                                     .Build()) | 
 |                   .packets, | 
 |               SizeIs(1)); | 
 |  | 
 |   RTC_UNUSED(packet_buffer.InsertPacket(Packet(kH264FuA) | 
 |                                             .Slice() | 
 |                                             .SeqNum(kBufferSize + 1) | 
 |                                             .Time(kBufferSize + 1) | 
 |                                             .AsFirstFragment() | 
 |                                             .Build())); | 
 |  | 
 |   RTC_UNUSED(packet_buffer.InsertPacket(Packet(kH264FuA) | 
 |                                             .Slice() | 
 |                                             .SeqNum(kBufferSize + 3) | 
 |                                             .Time(kBufferSize + 1) | 
 |                                             .Marker() | 
 |                                             .Build())); | 
 |   EXPECT_THAT( | 
 |       packet_buffer | 
 |           .InsertPacket(Packet(kH264FuA).Slice().SeqNum(2).Time(2).Build()) | 
 |           .packets, | 
 |       IsEmpty()); | 
 |  | 
 |   EXPECT_THAT(packet_buffer | 
 |                   .InsertPacket(Packet(kH264FuA) | 
 |                                     .Slice() | 
 |                                     .SeqNum(kBufferSize + 2) | 
 |                                     .Time(kBufferSize + 1) | 
 |                                     .Build()) | 
 |                   .packets, | 
 |               SizeIs(3)); | 
 | } | 
 |  | 
 | TEST(H264PacketBufferTest, OldPacketDoesntCompleteFrame) { | 
 |   H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); | 
 |  | 
 |   EXPECT_THAT(packet_buffer | 
 |                   .InsertPacket(Packet(kH264StapA) | 
 |                                     .Sps() | 
 |                                     .Pps() | 
 |                                     .Idr() | 
 |                                     .SeqNum(kBufferSize) | 
 |                                     .Time(kBufferSize) | 
 |                                     .Marker() | 
 |                                     .Build()) | 
 |                   .packets, | 
 |               SizeIs(1)); | 
 |  | 
 |   EXPECT_THAT(packet_buffer | 
 |                   .InsertPacket(Packet(kH264FuA) | 
 |                                     .Slice() | 
 |                                     .SeqNum(kBufferSize + 3) | 
 |                                     .Time(kBufferSize + 1) | 
 |                                     .Marker() | 
 |                                     .Build()) | 
 |                   .packets, | 
 |               IsEmpty()); | 
 |  | 
 |   EXPECT_THAT( | 
 |       packet_buffer | 
 |           .InsertPacket( | 
 |               Packet(kH264FuA).Slice().SeqNum(2).Time(2).Marker().Build()) | 
 |           .packets, | 
 |       IsEmpty()); | 
 |  | 
 |   EXPECT_THAT(packet_buffer | 
 |                   .InsertPacket(Packet(kH264FuA) | 
 |                                     .Slice() | 
 |                                     .SeqNum(kBufferSize + 1) | 
 |                                     .Time(kBufferSize + 1) | 
 |                                     .AsFirstFragment() | 
 |                                     .Build()) | 
 |                   .packets, | 
 |               IsEmpty()); | 
 | } | 
 |  | 
 | TEST(H264PacketBufferTest, FrameBoundariesAreSet) { | 
 |   H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); | 
 |  | 
 |   auto key = packet_buffer.InsertPacket( | 
 |       Packet(kH264StapA).Sps().Pps().Idr().SeqNum(1).Time(1).Marker().Build()); | 
 |  | 
 |   ASSERT_THAT(key.packets, SizeIs(1)); | 
 |   EXPECT_TRUE(key.packets[0]->video_header.is_first_packet_in_frame); | 
 |   EXPECT_TRUE(key.packets[0]->video_header.is_last_packet_in_frame); | 
 |  | 
 |   RTC_UNUSED(packet_buffer.InsertPacket( | 
 |       Packet(kH264FuA).Slice().SeqNum(2).Time(2).Build())); | 
 |   RTC_UNUSED(packet_buffer.InsertPacket( | 
 |       Packet(kH264FuA).Slice().SeqNum(3).Time(2).Build())); | 
 |   auto delta = packet_buffer.InsertPacket( | 
 |       Packet(kH264FuA).Slice().SeqNum(4).Time(2).Marker().Build()); | 
 |  | 
 |   ASSERT_THAT(delta.packets, SizeIs(3)); | 
 |   EXPECT_TRUE(delta.packets[0]->video_header.is_first_packet_in_frame); | 
 |   EXPECT_FALSE(delta.packets[0]->video_header.is_last_packet_in_frame); | 
 |  | 
 |   EXPECT_FALSE(delta.packets[1]->video_header.is_first_packet_in_frame); | 
 |   EXPECT_FALSE(delta.packets[1]->video_header.is_last_packet_in_frame); | 
 |  | 
 |   EXPECT_FALSE(delta.packets[2]->video_header.is_first_packet_in_frame); | 
 |   EXPECT_TRUE(delta.packets[2]->video_header.is_last_packet_in_frame); | 
 | } | 
 |  | 
 | TEST(H264PacketBufferTest, ResolutionSetOnFirstPacket) { | 
 |   H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); | 
 |  | 
 |   RTC_UNUSED(packet_buffer.InsertPacket( | 
 |       Packet(kH264SingleNalu).Aud().SeqNum(1).Time(1).Build())); | 
 |   auto res = packet_buffer.InsertPacket(Packet(kH264StapA) | 
 |                                             .SpsWithResolution({320, 240}) | 
 |                                             .Pps() | 
 |                                             .Idr() | 
 |                                             .SeqNum(2) | 
 |                                             .Time(1) | 
 |                                             .Marker() | 
 |                                             .Build()); | 
 |  | 
 |   ASSERT_THAT(res.packets, SizeIs(2)); | 
 |   EXPECT_THAT(res.packets[0]->video_header.width, Eq(320)); | 
 |   EXPECT_THAT(res.packets[0]->video_header.height, Eq(240)); | 
 | } | 
 |  | 
 | TEST(H264PacketBufferTest, KeyframeAndDeltaFrameSetOnFirstPacket) { | 
 |   H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); | 
 |  | 
 |   RTC_UNUSED(packet_buffer.InsertPacket( | 
 |       Packet(kH264SingleNalu).Aud().SeqNum(1).Time(1).Build())); | 
 |   auto key = packet_buffer.InsertPacket( | 
 |       Packet(kH264StapA).Sps().Pps().Idr().SeqNum(2).Time(1).Marker().Build()); | 
 |  | 
 |   auto delta = packet_buffer.InsertPacket( | 
 |       Packet(kH264SingleNalu).Slice().SeqNum(3).Time(2).Marker().Build()); | 
 |  | 
 |   ASSERT_THAT(key.packets, SizeIs(2)); | 
 |   EXPECT_THAT(key.packets[0]->video_header.frame_type, | 
 |               Eq(VideoFrameType::kVideoFrameKey)); | 
 |   ASSERT_THAT(delta.packets, SizeIs(1)); | 
 |   EXPECT_THAT(delta.packets[0]->video_header.frame_type, | 
 |               Eq(VideoFrameType::kVideoFrameDelta)); | 
 | } | 
 |  | 
 | TEST(H264PacketBufferTest, RtpSeqNumWrap) { | 
 |   H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); | 
 |  | 
 |   RTC_UNUSED(packet_buffer.InsertPacket( | 
 |       Packet(kH264StapA).Sps().Pps().SeqNum(0xffff).Time(0).Build())); | 
 |  | 
 |   RTC_UNUSED(packet_buffer.InsertPacket( | 
 |       Packet(kH264FuA).Idr().SeqNum(0).Time(0).Build())); | 
 |   EXPECT_THAT(packet_buffer | 
 |                   .InsertPacket( | 
 |                       Packet(kH264FuA).Idr().SeqNum(1).Time(0).Marker().Build()) | 
 |                   .packets, | 
 |               SizeIs(3)); | 
 | } | 
 |  | 
 | TEST(H264PacketBufferTest, StapAFixedBitstream) { | 
 |   H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); | 
 |  | 
 |   auto packets = packet_buffer | 
 |                      .InsertPacket(Packet(kH264StapA) | 
 |                                        .Sps({1, 2, 3}) | 
 |                                        .Pps({4, 5, 6}) | 
 |                                        .Idr({7, 8, 9}) | 
 |                                        .SeqNum(0) | 
 |                                        .Time(0) | 
 |                                        .Marker() | 
 |                                        .Build()) | 
 |                      .packets; | 
 |  | 
 |   ASSERT_THAT(packets, SizeIs(1)); | 
 |   EXPECT_THAT(PacketPayload(packets[0]), | 
 |               ElementsAreArray(FlatVector({StartCode(), | 
 |                                            {kSps, 1, 2, 3}, | 
 |                                            StartCode(), | 
 |                                            {kPps, 4, 5, 6}, | 
 |                                            StartCode(), | 
 |                                            {kIdr, 7, 8, 9}}))); | 
 | } | 
 |  | 
 | TEST(H264PacketBufferTest, SingleNaluFixedBitstream) { | 
 |   H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); | 
 |  | 
 |   RTC_UNUSED(packet_buffer.InsertPacket( | 
 |       Packet(kH264SingleNalu).Sps({1, 2, 3}).SeqNum(0).Time(0).Build())); | 
 |   RTC_UNUSED(packet_buffer.InsertPacket( | 
 |       Packet(kH264SingleNalu).Pps({4, 5, 6}).SeqNum(1).Time(0).Build())); | 
 |   auto packets = packet_buffer | 
 |                      .InsertPacket(Packet(kH264SingleNalu) | 
 |                                        .Idr({7, 8, 9}) | 
 |                                        .SeqNum(2) | 
 |                                        .Time(0) | 
 |                                        .Marker() | 
 |                                        .Build()) | 
 |                      .packets; | 
 |  | 
 |   ASSERT_THAT(packets, SizeIs(3)); | 
 |   EXPECT_THAT(PacketPayload(packets[0]), | 
 |               ElementsAreArray(FlatVector({StartCode(), {kSps, 1, 2, 3}}))); | 
 |   EXPECT_THAT(PacketPayload(packets[1]), | 
 |               ElementsAreArray(FlatVector({StartCode(), {kPps, 4, 5, 6}}))); | 
 |   EXPECT_THAT(PacketPayload(packets[2]), | 
 |               ElementsAreArray(FlatVector({StartCode(), {kIdr, 7, 8, 9}}))); | 
 | } | 
 |  | 
 | TEST(H264PacketBufferTest, StapaAndFuaFixedBitstream) { | 
 |   H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); | 
 |  | 
 |   RTC_UNUSED(packet_buffer.InsertPacket(Packet(kH264StapA) | 
 |                                             .Sps({1, 2, 3}) | 
 |                                             .Pps({4, 5, 6}) | 
 |                                             .SeqNum(0) | 
 |                                             .Time(0) | 
 |                                             .Build())); | 
 |   RTC_UNUSED(packet_buffer.InsertPacket(Packet(kH264FuA) | 
 |                                             .Idr({8, 8, 8}) | 
 |                                             .SeqNum(1) | 
 |                                             .Time(0) | 
 |                                             .AsFirstFragment() | 
 |                                             .Build())); | 
 |   auto packets = packet_buffer | 
 |                      .InsertPacket(Packet(kH264FuA) | 
 |                                        .Idr({9, 9, 9}) | 
 |                                        .SeqNum(2) | 
 |                                        .Time(0) | 
 |                                        .Marker() | 
 |                                        .Build()) | 
 |                      .packets; | 
 |  | 
 |   ASSERT_THAT(packets, SizeIs(3)); | 
 |   EXPECT_THAT( | 
 |       PacketPayload(packets[0]), | 
 |       ElementsAreArray(FlatVector( | 
 |           {StartCode(), {kSps, 1, 2, 3}, StartCode(), {kPps, 4, 5, 6}}))); | 
 |   EXPECT_THAT(PacketPayload(packets[1]), | 
 |               ElementsAreArray(FlatVector({StartCode(), {8, 8, 8}}))); | 
 |   // Third is a continuation of second, so only the payload is expected. | 
 |   EXPECT_THAT(PacketPayload(packets[2]), | 
 |               ElementsAreArray(FlatVector({{9, 9, 9}}))); | 
 | } | 
 |  | 
 | TEST(H264PacketBufferTest, FullPacketBufferDoesNotBlockKeyframe) { | 
 |   H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); | 
 |  | 
 |   for (int i = 0; i < kBufferSize; ++i) { | 
 |     EXPECT_THAT( | 
 |         packet_buffer | 
 |             .InsertPacket( | 
 |                 Packet(kH264SingleNalu).Slice().SeqNum(i).Time(0).Build()) | 
 |             .packets, | 
 |         IsEmpty()); | 
 |   } | 
 |  | 
 |   EXPECT_THAT(packet_buffer | 
 |                   .InsertPacket(Packet(kH264StapA) | 
 |                                     .Sps() | 
 |                                     .Pps() | 
 |                                     .Idr() | 
 |                                     .SeqNum(kBufferSize) | 
 |                                     .Time(1) | 
 |                                     .Marker() | 
 |                                     .Build()) | 
 |                   .packets, | 
 |               SizeIs(1)); | 
 | } | 
 |  | 
 | TEST(H264PacketBufferTest, TooManyNalusInPacket) { | 
 |   H264PacketBuffer packet_buffer(/*allow_idr_only_keyframes=*/false); | 
 |  | 
 |   std::unique_ptr<H264PacketBuffer::Packet> packet( | 
 |       Packet(kH264StapA).Sps().Pps().Idr().SeqNum(1).Time(1).Marker().Build()); | 
 |   auto& h264_header = | 
 |       absl::get<RTPVideoHeaderH264>(packet->video_header.video_type_header); | 
 |   h264_header.nalus_length = kMaxNalusPerPacket + 1; | 
 |  | 
 |   EXPECT_THAT(packet_buffer.InsertPacket(std::move(packet)).packets, IsEmpty()); | 
 | } | 
 |  | 
 | }  // namespace | 
 | }  // namespace webrtc |