diff options
Diffstat (limited to 'third_party/libwebrtc/modules/video_coding/packet_buffer_unittest.cc')
-rw-r--r-- | third_party/libwebrtc/modules/video_coding/packet_buffer_unittest.cc | 860 |
1 files changed, 860 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/video_coding/packet_buffer_unittest.cc b/third_party/libwebrtc/modules/video_coding/packet_buffer_unittest.cc new file mode 100644 index 0000000000..607ec47dc4 --- /dev/null +++ b/third_party/libwebrtc/modules/video_coding/packet_buffer_unittest.cc @@ -0,0 +1,860 @@ +/* + * Copyright (c) 2016 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/packet_buffer.h" + +#include <cstring> +#include <limits> +#include <ostream> +#include <string> +#include <utility> + +#include "api/array_view.h" +#include "common_video/h264/h264_common.h" +#include "modules/rtp_rtcp/source/frame_object.h" +#include "rtc_base/numerics/sequence_number_unwrapper.h" +#include "rtc_base/random.h" +#include "test/field_trial.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { +namespace video_coding { +namespace { + +using ::testing::ElementsAre; +using ::testing::ElementsAreArray; +using ::testing::IsEmpty; +using ::testing::Matches; +using ::testing::Pointee; +using ::testing::SizeIs; + +constexpr int kStartSize = 16; +constexpr int kMaxSize = 64; + +void IgnoreResult(PacketBuffer::InsertResult /*result*/) {} + +// Validates frame boundaries are valid and returns first sequence_number for +// each frame. +std::vector<uint16_t> StartSeqNums( + rtc::ArrayView<const std::unique_ptr<PacketBuffer::Packet>> packets) { + std::vector<uint16_t> result; + bool frame_boundary = true; + for (const auto& packet : packets) { + EXPECT_EQ(frame_boundary, packet->is_first_packet_in_frame()); + if (packet->is_first_packet_in_frame()) { + result.push_back(packet->seq_num); + } + frame_boundary = packet->is_last_packet_in_frame(); + } + EXPECT_TRUE(frame_boundary); + return result; +} + +MATCHER_P(StartSeqNumsAre, seq_num, "") { + return Matches(ElementsAre(seq_num))(StartSeqNums(arg.packets)); +} + +MATCHER_P2(StartSeqNumsAre, seq_num1, seq_num2, "") { + return Matches(ElementsAre(seq_num1, seq_num2))(StartSeqNums(arg.packets)); +} + +MATCHER(KeyFrame, "") { + return arg->is_first_packet_in_frame() && + arg->video_header.frame_type == VideoFrameType::kVideoFrameKey; +} + +MATCHER(DeltaFrame, "") { + return arg->is_first_packet_in_frame() && + arg->video_header.frame_type == VideoFrameType::kVideoFrameDelta; +} + +struct PacketBufferInsertResult : public PacketBuffer::InsertResult { + explicit PacketBufferInsertResult(PacketBuffer::InsertResult result) + : InsertResult(std::move(result)) {} +}; + +void PrintTo(const PacketBufferInsertResult& result, std::ostream* os) { + *os << "frames: { "; + for (const auto& packet : result.packets) { + if (packet->is_first_packet_in_frame() && + packet->is_last_packet_in_frame()) { + *os << "{sn: " << packet->seq_num << " }"; + } else if (packet->is_first_packet_in_frame()) { + *os << "{sn: [" << packet->seq_num << "-"; + } else if (packet->is_last_packet_in_frame()) { + *os << packet->seq_num << "] }, "; + } + } + *os << " }"; + if (result.buffer_cleared) { + *os << ", buffer_cleared"; + } +} + +class PacketBufferTest : public ::testing::Test { + protected: + PacketBufferTest() : rand_(0x7732213), packet_buffer_(kStartSize, kMaxSize) {} + + uint16_t Rand() { return rand_.Rand<uint16_t>(); } + + enum IsKeyFrame { kKeyFrame, kDeltaFrame }; + enum IsFirst { kFirst, kNotFirst }; + enum IsLast { kLast, kNotLast }; + + PacketBufferInsertResult Insert(uint16_t seq_num, // packet sequence number + IsKeyFrame keyframe, // is keyframe + IsFirst first, // is first packet of frame + IsLast last, // is last packet of frame + rtc::ArrayView<const uint8_t> data = {}, + uint32_t timestamp = 123u) { // rtp timestamp + auto packet = std::make_unique<PacketBuffer::Packet>(); + packet->video_header.codec = kVideoCodecGeneric; + packet->timestamp = timestamp; + packet->seq_num = seq_num; + packet->video_header.frame_type = keyframe == kKeyFrame + ? VideoFrameType::kVideoFrameKey + : VideoFrameType::kVideoFrameDelta; + packet->video_header.is_first_packet_in_frame = first == kFirst; + packet->video_header.is_last_packet_in_frame = last == kLast; + packet->video_payload.SetData(data.data(), data.size()); + + return PacketBufferInsertResult( + packet_buffer_.InsertPacket(std::move(packet))); + } + + Random rand_; + PacketBuffer packet_buffer_; +}; + +TEST_F(PacketBufferTest, InsertOnePacket) { + const uint16_t seq_num = Rand(); + EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kLast).packets, SizeIs(1)); +} + +TEST_F(PacketBufferTest, InsertMultiplePackets) { + const uint16_t seq_num = Rand(); + EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kLast).packets, SizeIs(1)); + EXPECT_THAT(Insert(seq_num + 1, kKeyFrame, kFirst, kLast).packets, SizeIs(1)); + EXPECT_THAT(Insert(seq_num + 2, kKeyFrame, kFirst, kLast).packets, SizeIs(1)); + EXPECT_THAT(Insert(seq_num + 3, kKeyFrame, kFirst, kLast).packets, SizeIs(1)); +} + +TEST_F(PacketBufferTest, InsertDuplicatePacket) { + const uint16_t seq_num = Rand(); + EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kNotLast).packets, IsEmpty()); + EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kNotLast).packets, IsEmpty()); + EXPECT_THAT(Insert(seq_num + 1, kKeyFrame, kNotFirst, kLast).packets, + SizeIs(2)); +} + +TEST_F(PacketBufferTest, SeqNumWrapOneFrame) { + Insert(0xFFFF, kKeyFrame, kFirst, kNotLast); + EXPECT_THAT(Insert(0x0, kKeyFrame, kNotFirst, kLast), + StartSeqNumsAre(0xFFFF)); +} + +TEST_F(PacketBufferTest, SeqNumWrapTwoFrames) { + EXPECT_THAT(Insert(0xFFFF, kKeyFrame, kFirst, kLast), + StartSeqNumsAre(0xFFFF)); + EXPECT_THAT(Insert(0x0, kKeyFrame, kFirst, kLast), StartSeqNumsAre(0x0)); +} + +TEST_F(PacketBufferTest, InsertOldPackets) { + EXPECT_THAT(Insert(100, kKeyFrame, kFirst, kNotLast).packets, IsEmpty()); + EXPECT_THAT(Insert(102, kDeltaFrame, kFirst, kLast).packets, SizeIs(1)); + EXPECT_THAT(Insert(101, kKeyFrame, kNotFirst, kLast).packets, SizeIs(2)); + + EXPECT_THAT(Insert(100, kKeyFrame, kFirst, kNotLast).packets, IsEmpty()); + EXPECT_THAT(Insert(100, kKeyFrame, kFirst, kNotLast).packets, IsEmpty()); + EXPECT_THAT(Insert(102, kDeltaFrame, kFirst, kLast).packets, SizeIs(1)); + + packet_buffer_.ClearTo(102); + EXPECT_THAT(Insert(102, kDeltaFrame, kFirst, kLast).packets, IsEmpty()); + EXPECT_THAT(Insert(103, kDeltaFrame, kFirst, kLast).packets, SizeIs(1)); +} + +TEST_F(PacketBufferTest, FrameSize) { + const uint16_t seq_num = Rand(); + uint8_t data1[5] = {}; + uint8_t data2[5] = {}; + uint8_t data3[5] = {}; + uint8_t data4[5] = {}; + + Insert(seq_num, kKeyFrame, kFirst, kNotLast, data1); + Insert(seq_num + 1, kKeyFrame, kNotFirst, kNotLast, data2); + Insert(seq_num + 2, kKeyFrame, kNotFirst, kNotLast, data3); + auto packets = + Insert(seq_num + 3, kKeyFrame, kNotFirst, kLast, data4).packets; + // Expect one frame of 4 packets. + EXPECT_THAT(StartSeqNums(packets), ElementsAre(seq_num)); + EXPECT_THAT(packets, SizeIs(4)); +} + +TEST_F(PacketBufferTest, ExpandBuffer) { + const uint16_t seq_num = Rand(); + + Insert(seq_num, kKeyFrame, kFirst, kNotLast); + for (int i = 1; i < kStartSize; ++i) + EXPECT_FALSE( + Insert(seq_num + i, kKeyFrame, kNotFirst, kNotLast).buffer_cleared); + + // Already inserted kStartSize number of packets, inserting the last packet + // should increase the buffer size and also result in an assembled frame. + EXPECT_FALSE( + Insert(seq_num + kStartSize, kKeyFrame, kNotFirst, kLast).buffer_cleared); +} + +TEST_F(PacketBufferTest, SingleFrameExpandsBuffer) { + const uint16_t seq_num = Rand(); + + Insert(seq_num, kKeyFrame, kFirst, kNotLast); + for (int i = 1; i < kStartSize; ++i) + Insert(seq_num + i, kKeyFrame, kNotFirst, kNotLast); + EXPECT_THAT(Insert(seq_num + kStartSize, kKeyFrame, kNotFirst, kLast), + StartSeqNumsAre(seq_num)); +} + +TEST_F(PacketBufferTest, ExpandBufferOverflow) { + const uint16_t seq_num = Rand(); + + EXPECT_FALSE(Insert(seq_num, kKeyFrame, kFirst, kNotLast).buffer_cleared); + for (int i = 1; i < kMaxSize; ++i) + EXPECT_FALSE( + Insert(seq_num + i, kKeyFrame, kNotFirst, kNotLast).buffer_cleared); + + // Already inserted kMaxSize number of packets, inserting the last packet + // should overflow the buffer and result in false being returned. + EXPECT_TRUE( + Insert(seq_num + kMaxSize, kKeyFrame, kNotFirst, kLast).buffer_cleared); +} + +TEST_F(PacketBufferTest, OnePacketOneFrame) { + const uint16_t seq_num = Rand(); + EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kLast), + StartSeqNumsAre(seq_num)); +} + +TEST_F(PacketBufferTest, TwoPacketsTwoFrames) { + const uint16_t seq_num = Rand(); + + EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kLast), + StartSeqNumsAre(seq_num)); + EXPECT_THAT(Insert(seq_num + 1, kKeyFrame, kFirst, kLast), + StartSeqNumsAre(seq_num + 1)); +} + +TEST_F(PacketBufferTest, TwoPacketsOneFrames) { + const uint16_t seq_num = Rand(); + + EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kNotLast).packets, IsEmpty()); + EXPECT_THAT(Insert(seq_num + 1, kKeyFrame, kNotFirst, kLast), + StartSeqNumsAre(seq_num)); +} + +TEST_F(PacketBufferTest, ThreePacketReorderingOneFrame) { + const uint16_t seq_num = Rand(); + + EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kNotLast).packets, IsEmpty()); + EXPECT_THAT(Insert(seq_num + 2, kKeyFrame, kNotFirst, kLast).packets, + IsEmpty()); + EXPECT_THAT(Insert(seq_num + 1, kKeyFrame, kNotFirst, kNotLast), + StartSeqNumsAre(seq_num)); +} + +TEST_F(PacketBufferTest, Frames) { + const uint16_t seq_num = Rand(); + + EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kLast), + StartSeqNumsAre(seq_num)); + EXPECT_THAT(Insert(seq_num + 1, kDeltaFrame, kFirst, kLast), + StartSeqNumsAre(seq_num + 1)); + EXPECT_THAT(Insert(seq_num + 2, kDeltaFrame, kFirst, kLast), + StartSeqNumsAre(seq_num + 2)); + EXPECT_THAT(Insert(seq_num + 3, kDeltaFrame, kFirst, kLast), + StartSeqNumsAre(seq_num + 3)); +} + +TEST_F(PacketBufferTest, ClearSinglePacket) { + const uint16_t seq_num = Rand(); + + for (int i = 0; i < kMaxSize; ++i) + Insert(seq_num + i, kDeltaFrame, kFirst, kLast); + + packet_buffer_.ClearTo(seq_num); + EXPECT_FALSE( + Insert(seq_num + kMaxSize, kDeltaFrame, kFirst, kLast).buffer_cleared); +} + +TEST_F(PacketBufferTest, ClearPacketBeforeFullyReceivedFrame) { + Insert(0, kKeyFrame, kFirst, kNotLast); + Insert(1, kKeyFrame, kNotFirst, kNotLast); + packet_buffer_.ClearTo(0); + EXPECT_THAT(Insert(2, kKeyFrame, kNotFirst, kLast).packets, IsEmpty()); +} + +TEST_F(PacketBufferTest, ClearFullBuffer) { + for (int i = 0; i < kMaxSize; ++i) + Insert(i, kDeltaFrame, kFirst, kLast); + + packet_buffer_.ClearTo(kMaxSize - 1); + + for (int i = kMaxSize; i < 2 * kMaxSize; ++i) + EXPECT_FALSE(Insert(i, kDeltaFrame, kFirst, kLast).buffer_cleared); +} + +TEST_F(PacketBufferTest, DontClearNewerPacket) { + EXPECT_THAT(Insert(0, kKeyFrame, kFirst, kLast), StartSeqNumsAre(0)); + packet_buffer_.ClearTo(0); + EXPECT_THAT(Insert(2 * kStartSize, kKeyFrame, kFirst, kLast), + StartSeqNumsAre(2 * kStartSize)); + EXPECT_THAT(Insert(3 * kStartSize + 1, kKeyFrame, kFirst, kNotLast).packets, + IsEmpty()); + packet_buffer_.ClearTo(2 * kStartSize); + EXPECT_THAT(Insert(3 * kStartSize + 2, kKeyFrame, kNotFirst, kLast), + StartSeqNumsAre(3 * kStartSize + 1)); +} + +TEST_F(PacketBufferTest, OneIncompleteFrame) { + const uint16_t seq_num = Rand(); + + EXPECT_THAT(Insert(seq_num, kDeltaFrame, kFirst, kNotLast).packets, + IsEmpty()); + EXPECT_THAT(Insert(seq_num + 1, kDeltaFrame, kNotFirst, kLast), + StartSeqNumsAre(seq_num)); + EXPECT_THAT(Insert(seq_num - 1, kDeltaFrame, kNotFirst, kLast).packets, + IsEmpty()); +} + +TEST_F(PacketBufferTest, TwoIncompleteFramesFullBuffer) { + const uint16_t seq_num = Rand(); + + for (int i = 1; i < kMaxSize - 1; ++i) + Insert(seq_num + i, kDeltaFrame, kNotFirst, kNotLast); + EXPECT_THAT(Insert(seq_num, kDeltaFrame, kFirst, kNotLast).packets, + IsEmpty()); + EXPECT_THAT(Insert(seq_num - 1, kDeltaFrame, kNotFirst, kLast).packets, + IsEmpty()); +} + +TEST_F(PacketBufferTest, FramesReordered) { + const uint16_t seq_num = Rand(); + + EXPECT_THAT(Insert(seq_num + 1, kDeltaFrame, kFirst, kLast), + StartSeqNumsAre(seq_num + 1)); + EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kLast), + StartSeqNumsAre(seq_num)); + EXPECT_THAT(Insert(seq_num + 3, kDeltaFrame, kFirst, kLast), + StartSeqNumsAre(seq_num + 3)); + EXPECT_THAT(Insert(seq_num + 2, kDeltaFrame, kFirst, kLast), + StartSeqNumsAre(seq_num + 2)); +} + +TEST_F(PacketBufferTest, InsertPacketAfterSequenceNumberWrapAround) { + uint16_t kFirstSeqNum = 0; + uint32_t kTimestampDelta = 100; + uint32_t timestamp = 10000; + uint16_t seq_num = kFirstSeqNum; + + // Loop until seq_num wraps around. + SeqNumUnwrapper<uint16_t> unwrapper; + while (unwrapper.Unwrap(seq_num) < std::numeric_limits<uint16_t>::max()) { + Insert(seq_num++, kKeyFrame, kFirst, kNotLast, {}, timestamp); + for (int i = 0; i < 5; ++i) { + Insert(seq_num++, kKeyFrame, kNotFirst, kNotLast, {}, timestamp); + } + Insert(seq_num++, kKeyFrame, kNotFirst, kLast, {}, timestamp); + timestamp += kTimestampDelta; + } + + // Receive frame with overlapping sequence numbers. + Insert(seq_num++, kKeyFrame, kFirst, kNotLast, {}, timestamp); + for (int i = 0; i < 5; ++i) { + Insert(seq_num++, kKeyFrame, kNotFirst, kNotLast, {}, timestamp); + } + auto packets = + Insert(seq_num++, kKeyFrame, kNotFirst, kLast, {}, timestamp).packets; + // One frame of 7 packets. + EXPECT_THAT(StartSeqNums(packets), SizeIs(1)); + EXPECT_THAT(packets, SizeIs(7)); +} + +// If `sps_pps_idr_is_keyframe` is true, we require keyframes to contain +// SPS/PPS/IDR and the keyframes we create as part of the test do contain +// SPS/PPS/IDR. If `sps_pps_idr_is_keyframe` is false, we only require and +// create keyframes containing only IDR. +class PacketBufferH264Test : public PacketBufferTest { + protected: + explicit PacketBufferH264Test(bool sps_pps_idr_is_keyframe) + : PacketBufferTest(), sps_pps_idr_is_keyframe_(sps_pps_idr_is_keyframe) { + if (sps_pps_idr_is_keyframe) { + packet_buffer_.ForceSpsPpsIdrIsH264Keyframe(); + } + } + + PacketBufferInsertResult InsertH264( + uint16_t seq_num, // packet sequence number + IsKeyFrame keyframe, // is keyframe + IsFirst first, // is first packet of frame + IsLast last, // is last packet of frame + uint32_t timestamp, // rtp timestamp + rtc::ArrayView<const uint8_t> data = {}, + uint32_t width = 0, // width of frame (SPS/IDR) + uint32_t height = 0, // height of frame (SPS/IDR) + bool generic = false) { // has generic descriptor + auto packet = std::make_unique<PacketBuffer::Packet>(); + packet->video_header.codec = kVideoCodecH264; + auto& h264_header = + packet->video_header.video_type_header.emplace<RTPVideoHeaderH264>(); + packet->seq_num = seq_num; + packet->timestamp = timestamp; + if (keyframe == kKeyFrame) { + if (sps_pps_idr_is_keyframe_) { + h264_header.nalus[0].type = H264::NaluType::kSps; + h264_header.nalus[1].type = H264::NaluType::kPps; + h264_header.nalus[2].type = H264::NaluType::kIdr; + h264_header.nalus_length = 3; + } else { + h264_header.nalus[0].type = H264::NaluType::kIdr; + h264_header.nalus_length = 1; + } + } + packet->video_header.width = width; + packet->video_header.height = height; + packet->video_header.is_first_packet_in_frame = first == kFirst; + packet->video_header.is_last_packet_in_frame = last == kLast; + if (generic) { + packet->video_header.generic.emplace(); + } + packet->video_payload.SetData(data.data(), data.size()); + + return PacketBufferInsertResult( + packet_buffer_.InsertPacket(std::move(packet))); + } + + PacketBufferInsertResult InsertH264KeyFrameWithAud( + uint16_t seq_num, // packet sequence number + IsKeyFrame keyframe, // is keyframe + IsFirst first, // is first packet of frame + IsLast last, // is last packet of frame + uint32_t timestamp, // rtp timestamp + rtc::ArrayView<const uint8_t> data = {}, + uint32_t width = 0, // width of frame (SPS/IDR) + uint32_t height = 0) { // height of frame (SPS/IDR) + auto packet = std::make_unique<PacketBuffer::Packet>(); + packet->video_header.codec = kVideoCodecH264; + auto& h264_header = + packet->video_header.video_type_header.emplace<RTPVideoHeaderH264>(); + packet->seq_num = seq_num; + packet->timestamp = timestamp; + + // this should be the start of frame. + RTC_CHECK(first == kFirst); + + // Insert a AUD NALU / packet without width/height. + h264_header.nalus[0].type = H264::NaluType::kAud; + h264_header.nalus_length = 1; + packet->video_header.is_first_packet_in_frame = true; + packet->video_header.is_last_packet_in_frame = false; + IgnoreResult(packet_buffer_.InsertPacket(std::move(packet))); + // insert IDR + return InsertH264(seq_num + 1, keyframe, kNotFirst, last, timestamp, data, + width, height); + } + + const bool sps_pps_idr_is_keyframe_; +}; + +// This fixture is used to test the general behaviour of the packet buffer +// in both configurations. +class PacketBufferH264ParameterizedTest + : public ::testing::WithParamInterface<bool>, + public PacketBufferH264Test { + protected: + PacketBufferH264ParameterizedTest() : PacketBufferH264Test(GetParam()) {} +}; + +INSTANTIATE_TEST_SUITE_P(SpsPpsIdrIsKeyframe, + PacketBufferH264ParameterizedTest, + ::testing::Bool()); + +TEST_P(PacketBufferH264ParameterizedTest, DontRemoveMissingPacketOnClearTo) { + InsertH264(0, kKeyFrame, kFirst, kLast, 0); + InsertH264(2, kDeltaFrame, kFirst, kNotLast, 2); + packet_buffer_.ClearTo(0); + // Expect no frame because of missing of packet #1 + EXPECT_THAT(InsertH264(3, kDeltaFrame, kNotFirst, kLast, 2).packets, + IsEmpty()); +} + +TEST_P(PacketBufferH264ParameterizedTest, GetBitstreamOneFrameFullBuffer) { + uint8_t data_arr[kStartSize][1]; + uint8_t expected[kStartSize]; + + for (uint8_t i = 0; i < kStartSize; ++i) { + data_arr[i][0] = i; + expected[i] = i; + } + + InsertH264(0, kKeyFrame, kFirst, kNotLast, 1, data_arr[0]); + for (uint8_t i = 1; i < kStartSize - 1; ++i) { + InsertH264(i, kKeyFrame, kNotFirst, kNotLast, 1, data_arr[i]); + } + + auto packets = InsertH264(kStartSize - 1, kKeyFrame, kNotFirst, kLast, 1, + data_arr[kStartSize - 1]) + .packets; + ASSERT_THAT(StartSeqNums(packets), ElementsAre(0)); + EXPECT_THAT(packets, SizeIs(kStartSize)); + for (size_t i = 0; i < packets.size(); ++i) { + EXPECT_THAT(packets[i]->video_payload, SizeIs(1)) << "Packet #" << i; + } +} + +TEST_P(PacketBufferH264ParameterizedTest, GetBitstreamBufferPadding) { + uint16_t seq_num = Rand(); + rtc::CopyOnWriteBuffer data = "some plain old data"; + + auto packet = std::make_unique<PacketBuffer::Packet>(); + auto& h264_header = + packet->video_header.video_type_header.emplace<RTPVideoHeaderH264>(); + h264_header.nalus_length = 1; + h264_header.nalus[0].type = H264::NaluType::kIdr; + h264_header.packetization_type = kH264SingleNalu; + packet->seq_num = seq_num; + packet->video_header.codec = kVideoCodecH264; + packet->video_payload = data; + packet->video_header.is_first_packet_in_frame = true; + packet->video_header.is_last_packet_in_frame = true; + auto frames = packet_buffer_.InsertPacket(std::move(packet)).packets; + + ASSERT_THAT(frames, SizeIs(1)); + EXPECT_EQ(frames[0]->seq_num, seq_num); + EXPECT_EQ(frames[0]->video_payload, data); +} + +TEST_P(PacketBufferH264ParameterizedTest, FrameResolution) { + uint16_t seq_num = 100; + uint8_t data[] = "some plain old data"; + uint32_t width = 640; + uint32_t height = 360; + uint32_t timestamp = 1000; + + auto packets = InsertH264(seq_num, kKeyFrame, kFirst, kLast, timestamp, data, + width, height) + .packets; + + ASSERT_THAT(packets, SizeIs(1)); + EXPECT_EQ(packets[0]->video_header.width, width); + EXPECT_EQ(packets[0]->video_header.height, height); +} + +TEST_P(PacketBufferH264ParameterizedTest, FrameResolutionNaluBeforeSPS) { + uint16_t seq_num = 100; + uint8_t data[] = "some plain old data"; + uint32_t width = 640; + uint32_t height = 360; + uint32_t timestamp = 1000; + + auto packets = InsertH264KeyFrameWithAud(seq_num, kKeyFrame, kFirst, kLast, + timestamp, data, width, height) + .packets; + + ASSERT_THAT(StartSeqNums(packets), ElementsAre(seq_num)); + EXPECT_EQ(packets[0]->video_header.width, width); + EXPECT_EQ(packets[0]->video_header.height, height); +} + +TEST_F(PacketBufferTest, FreeSlotsOnFrameCreation) { + const uint16_t seq_num = Rand(); + + Insert(seq_num, kKeyFrame, kFirst, kNotLast); + Insert(seq_num + 1, kDeltaFrame, kNotFirst, kNotLast); + EXPECT_THAT(Insert(seq_num + 2, kDeltaFrame, kNotFirst, kLast), + StartSeqNumsAre(seq_num)); + + // Insert frame that fills the whole buffer. + Insert(seq_num + 3, kKeyFrame, kFirst, kNotLast); + for (int i = 0; i < kMaxSize - 2; ++i) + Insert(seq_num + i + 4, kDeltaFrame, kNotFirst, kNotLast); + EXPECT_THAT(Insert(seq_num + kMaxSize + 2, kKeyFrame, kNotFirst, kLast), + StartSeqNumsAre(seq_num + 3)); +} + +TEST_F(PacketBufferTest, Clear) { + const uint16_t seq_num = Rand(); + + Insert(seq_num, kKeyFrame, kFirst, kNotLast); + Insert(seq_num + 1, kDeltaFrame, kNotFirst, kNotLast); + EXPECT_THAT(Insert(seq_num + 2, kDeltaFrame, kNotFirst, kLast), + StartSeqNumsAre(seq_num)); + + packet_buffer_.Clear(); + + Insert(seq_num + kStartSize, kKeyFrame, kFirst, kNotLast); + Insert(seq_num + kStartSize + 1, kDeltaFrame, kNotFirst, kNotLast); + EXPECT_THAT(Insert(seq_num + kStartSize + 2, kDeltaFrame, kNotFirst, kLast), + StartSeqNumsAre(seq_num + kStartSize)); +} + +TEST_F(PacketBufferTest, FramesAfterClear) { + Insert(9025, kDeltaFrame, kFirst, kLast); + Insert(9024, kKeyFrame, kFirst, kLast); + packet_buffer_.ClearTo(9025); + EXPECT_THAT(Insert(9057, kDeltaFrame, kFirst, kLast).packets, SizeIs(1)); + EXPECT_THAT(Insert(9026, kDeltaFrame, kFirst, kLast).packets, SizeIs(1)); +} + +TEST_F(PacketBufferTest, SameFrameDifferentTimestamps) { + Insert(0, kKeyFrame, kFirst, kNotLast, {}, 1000); + EXPECT_THAT(Insert(1, kKeyFrame, kNotFirst, kLast, {}, 1001).packets, + IsEmpty()); +} + +TEST_F(PacketBufferTest, ContinuousSeqNumDoubleMarkerBit) { + Insert(2, kKeyFrame, kNotFirst, kNotLast); + Insert(1, kKeyFrame, kFirst, kLast); + EXPECT_THAT(Insert(3, kKeyFrame, kNotFirst, kLast).packets, IsEmpty()); +} + +TEST_F(PacketBufferTest, IncomingCodecChange) { + auto packet = std::make_unique<PacketBuffer::Packet>(); + packet->video_header.is_first_packet_in_frame = true; + packet->video_header.is_last_packet_in_frame = true; + packet->video_header.codec = kVideoCodecVP8; + packet->video_header.video_type_header.emplace<RTPVideoHeaderVP8>(); + packet->timestamp = 1; + packet->seq_num = 1; + packet->video_header.frame_type = VideoFrameType::kVideoFrameKey; + EXPECT_THAT(packet_buffer_.InsertPacket(std::move(packet)).packets, + SizeIs(1)); + + packet = std::make_unique<PacketBuffer::Packet>(); + packet->video_header.is_first_packet_in_frame = true; + packet->video_header.is_last_packet_in_frame = true; + packet->video_header.codec = kVideoCodecH264; + auto& h264_header = + packet->video_header.video_type_header.emplace<RTPVideoHeaderH264>(); + h264_header.nalus_length = 1; + packet->timestamp = 3; + packet->seq_num = 3; + packet->video_header.frame_type = VideoFrameType::kVideoFrameKey; + EXPECT_THAT(packet_buffer_.InsertPacket(std::move(packet)).packets, + IsEmpty()); + + packet = std::make_unique<PacketBuffer::Packet>(); + packet->video_header.is_first_packet_in_frame = true; + packet->video_header.is_last_packet_in_frame = true; + packet->video_header.codec = kVideoCodecVP8; + packet->video_header.video_type_header.emplace<RTPVideoHeaderVP8>(); + packet->timestamp = 2; + packet->seq_num = 2; + packet->video_header.frame_type = VideoFrameType::kVideoFrameDelta; + EXPECT_THAT(packet_buffer_.InsertPacket(std::move(packet)).packets, + SizeIs(2)); +} + +TEST_F(PacketBufferTest, TooManyNalusInPacket) { + auto packet = std::make_unique<PacketBuffer::Packet>(); + packet->video_header.codec = kVideoCodecH264; + packet->timestamp = 1; + packet->seq_num = 1; + packet->video_header.frame_type = VideoFrameType::kVideoFrameKey; + packet->video_header.is_first_packet_in_frame = true; + packet->video_header.is_last_packet_in_frame = true; + auto& h264_header = + packet->video_header.video_type_header.emplace<RTPVideoHeaderH264>(); + h264_header.nalus_length = kMaxNalusPerPacket; + EXPECT_THAT(packet_buffer_.InsertPacket(std::move(packet)).packets, + IsEmpty()); +} + +TEST_P(PacketBufferH264ParameterizedTest, OneFrameFillBuffer) { + InsertH264(0, kKeyFrame, kFirst, kNotLast, 1000); + for (int i = 1; i < kStartSize - 1; ++i) + InsertH264(i, kKeyFrame, kNotFirst, kNotLast, 1000); + EXPECT_THAT(InsertH264(kStartSize - 1, kKeyFrame, kNotFirst, kLast, 1000), + StartSeqNumsAre(0)); +} + +TEST_P(PacketBufferH264ParameterizedTest, CreateFramesAfterFilledBuffer) { + EXPECT_THAT(InsertH264(kStartSize - 2, kKeyFrame, kFirst, kLast, 0).packets, + SizeIs(1)); + + InsertH264(kStartSize, kDeltaFrame, kFirst, kNotLast, 2000); + for (int i = 1; i < kStartSize; ++i) + InsertH264(kStartSize + i, kDeltaFrame, kNotFirst, kNotLast, 2000); + EXPECT_THAT( + InsertH264(kStartSize + kStartSize, kDeltaFrame, kNotFirst, kLast, 2000) + .packets, + IsEmpty()); + + EXPECT_THAT(InsertH264(kStartSize - 1, kKeyFrame, kFirst, kLast, 1000), + StartSeqNumsAre(kStartSize - 1, kStartSize)); +} + +TEST_P(PacketBufferH264ParameterizedTest, OneFrameMaxSeqNum) { + InsertH264(65534, kKeyFrame, kFirst, kNotLast, 1000); + EXPECT_THAT(InsertH264(65535, kKeyFrame, kNotFirst, kLast, 1000), + StartSeqNumsAre(65534)); +} + +TEST_P(PacketBufferH264ParameterizedTest, InsertTooOldPackets) { + InsertH264(4660, kKeyFrame, kFirst, kNotLast, 1000); + InsertH264(37429, kDeltaFrame, kFirst, kNotLast, 1000); + InsertH264(4662, kKeyFrame, kFirst, kLast, 1000); +} + +TEST_P(PacketBufferH264ParameterizedTest, ClearMissingPacketsOnKeyframe) { + InsertH264(0, kKeyFrame, kFirst, kLast, 1000); + InsertH264(2, kKeyFrame, kFirst, kLast, 3000); + InsertH264(3, kDeltaFrame, kFirst, kNotLast, 4000); + InsertH264(4, kDeltaFrame, kNotFirst, kLast, 4000); + + EXPECT_THAT(InsertH264(kStartSize + 1, kKeyFrame, kFirst, kLast, 18000), + StartSeqNumsAre(kStartSize + 1)); +} + +TEST_P(PacketBufferH264ParameterizedTest, FindFramesOnPadding) { + EXPECT_THAT(InsertH264(0, kKeyFrame, kFirst, kLast, 1000), + StartSeqNumsAre(0)); + EXPECT_THAT(InsertH264(2, kDeltaFrame, kFirst, kLast, 1000).packets, + IsEmpty()); + + EXPECT_THAT(packet_buffer_.InsertPadding(1), StartSeqNumsAre(2)); +} + +TEST_P(PacketBufferH264ParameterizedTest, FindFramesOnReorderedPadding) { + EXPECT_THAT(InsertH264(0, kKeyFrame, kFirst, kLast, 1001), + StartSeqNumsAre(0)); + EXPECT_THAT(InsertH264(1, kDeltaFrame, kFirst, kNotLast, 1002).packets, + IsEmpty()); + EXPECT_THAT(packet_buffer_.InsertPadding(3).packets, IsEmpty()); + EXPECT_THAT(InsertH264(4, kDeltaFrame, kFirst, kLast, 1003).packets, + IsEmpty()); + EXPECT_THAT(InsertH264(2, kDeltaFrame, kNotFirst, kLast, 1002), + StartSeqNumsAre(1, 4)); +} + +class PacketBufferH264XIsKeyframeTest : public PacketBufferH264Test { + protected: + const uint16_t kSeqNum = 5; + + explicit PacketBufferH264XIsKeyframeTest(bool sps_pps_idr_is_keyframe) + : PacketBufferH264Test(sps_pps_idr_is_keyframe) {} + + std::unique_ptr<PacketBuffer::Packet> CreatePacket() { + auto packet = std::make_unique<PacketBuffer::Packet>(); + packet->video_header.codec = kVideoCodecH264; + packet->seq_num = kSeqNum; + + packet->video_header.is_first_packet_in_frame = true; + packet->video_header.is_last_packet_in_frame = true; + return packet; + } +}; + +class PacketBufferH264IdrIsKeyframeTest + : public PacketBufferH264XIsKeyframeTest { + protected: + PacketBufferH264IdrIsKeyframeTest() + : PacketBufferH264XIsKeyframeTest(false) {} +}; + +TEST_F(PacketBufferH264IdrIsKeyframeTest, IdrIsKeyframe) { + auto packet = CreatePacket(); + auto& h264_header = + packet->video_header.video_type_header.emplace<RTPVideoHeaderH264>(); + h264_header.nalus[0].type = H264::NaluType::kIdr; + h264_header.nalus_length = 1; + EXPECT_THAT(packet_buffer_.InsertPacket(std::move(packet)).packets, + ElementsAre(KeyFrame())); +} + +TEST_F(PacketBufferH264IdrIsKeyframeTest, SpsPpsIdrIsKeyframe) { + auto packet = CreatePacket(); + auto& h264_header = + packet->video_header.video_type_header.emplace<RTPVideoHeaderH264>(); + h264_header.nalus[0].type = H264::NaluType::kSps; + h264_header.nalus[1].type = H264::NaluType::kPps; + h264_header.nalus[2].type = H264::NaluType::kIdr; + h264_header.nalus_length = 3; + + EXPECT_THAT(packet_buffer_.InsertPacket(std::move(packet)).packets, + ElementsAre(KeyFrame())); +} + +class PacketBufferH264SpsPpsIdrIsKeyframeTest + : public PacketBufferH264XIsKeyframeTest { + protected: + PacketBufferH264SpsPpsIdrIsKeyframeTest() + : PacketBufferH264XIsKeyframeTest(true) {} +}; + +TEST_F(PacketBufferH264SpsPpsIdrIsKeyframeTest, IdrIsNotKeyframe) { + auto packet = CreatePacket(); + auto& h264_header = + packet->video_header.video_type_header.emplace<RTPVideoHeaderH264>(); + h264_header.nalus[0].type = H264::NaluType::kIdr; + h264_header.nalus_length = 1; + + EXPECT_THAT(packet_buffer_.InsertPacket(std::move(packet)).packets, + ElementsAre(DeltaFrame())); +} + +TEST_F(PacketBufferH264SpsPpsIdrIsKeyframeTest, SpsPpsIsNotKeyframe) { + auto packet = CreatePacket(); + auto& h264_header = + packet->video_header.video_type_header.emplace<RTPVideoHeaderH264>(); + h264_header.nalus[0].type = H264::NaluType::kSps; + h264_header.nalus[1].type = H264::NaluType::kPps; + h264_header.nalus_length = 2; + + EXPECT_THAT(packet_buffer_.InsertPacket(std::move(packet)).packets, + ElementsAre(DeltaFrame())); +} + +TEST_F(PacketBufferH264SpsPpsIdrIsKeyframeTest, SpsPpsIdrIsKeyframe) { + auto packet = CreatePacket(); + auto& h264_header = + packet->video_header.video_type_header.emplace<RTPVideoHeaderH264>(); + h264_header.nalus[0].type = H264::NaluType::kSps; + h264_header.nalus[1].type = H264::NaluType::kPps; + h264_header.nalus[2].type = H264::NaluType::kIdr; + h264_header.nalus_length = 3; + + EXPECT_THAT(packet_buffer_.InsertPacket(std::move(packet)).packets, + ElementsAre(KeyFrame())); +} + +class PacketBufferH264FrameGap : public PacketBufferH264Test { + protected: + PacketBufferH264FrameGap() : PacketBufferH264Test(true) {} +}; + +TEST_F(PacketBufferH264FrameGap, AllowFrameGapForH264WithGeneric) { + auto generic = true; + InsertH264(1, kKeyFrame, kFirst, kLast, 1001, {}, 0, 0, generic); + EXPECT_THAT(InsertH264(3, kDeltaFrame, kFirst, kLast, 1003, {}, 0, 0, generic) + .packets, + SizeIs(1)); +} + +TEST_F(PacketBufferH264FrameGap, DisallowFrameGapForH264NoGeneric) { + auto generic = false; + InsertH264(1, kKeyFrame, kFirst, kLast, 1001, {}, 0, 0, generic); + + EXPECT_THAT(InsertH264(3, kDeltaFrame, kFirst, kLast, 1003, {}, 0, 0, generic) + .packets, + IsEmpty()); +} + +} // namespace +} // namespace video_coding +} // namespace webrtc |