diff --git a/modules/video_coding/BUILD.gn b/modules/video_coding/BUILD.gn index ebf281faeb..49034d5392 100644 --- a/modules/video_coding/BUILD.gn +++ b/modules/video_coding/BUILD.gn @@ -953,6 +953,7 @@ if (rtc_include_tests) { "packet_buffer_unittest.cc", "receiver_unittest.cc", "rtp_frame_reference_finder_unittest.cc", + "rtp_vp8_ref_finder_unittest.cc", "rtp_vp9_ref_finder_unittest.cc", "session_info_unittest.cc", "test/stream_generator.cc", @@ -1008,6 +1009,7 @@ if (rtc_include_tests) { "../../api/task_queue:default_task_queue_factory", "../../api/test/video:function_video_factory", "../../api/video:builtin_video_bitrate_allocator_factory", + "../../api/video:encoded_frame", "../../api/video:video_adaptation", "../../api/video:video_bitrate_allocation", "../../api/video:video_bitrate_allocator", diff --git a/modules/video_coding/rtp_frame_reference_finder_unittest.cc b/modules/video_coding/rtp_frame_reference_finder_unittest.cc index 5cdad10bbd..f2ee9b576b 100644 --- a/modules/video_coding/rtp_frame_reference_finder_unittest.cc +++ b/modules/video_coding/rtp_frame_reference_finder_unittest.cc @@ -95,25 +95,6 @@ class TestRtpFrameReferenceFinder : public ::testing::Test, reference_finder_->ManageFrame(std::move(frame)); } - void InsertVp8(uint16_t seq_num_start, - uint16_t seq_num_end, - bool keyframe, - int32_t pid = kNoPictureId, - uint8_t tid = kNoTemporalIdx, - int32_t tl0 = kNoTl0PicIdx, - bool sync = false) { - RTPVideoHeaderVP8 vp8_header{}; - vp8_header.pictureId = pid % (1 << 15); - vp8_header.temporalIdx = tid; - vp8_header.tl0PicIdx = tl0; - vp8_header.layerSync = sync; - - std::unique_ptr frame = CreateFrame( - seq_num_start, seq_num_end, keyframe, kVideoCodecVP8, vp8_header); - - reference_finder_->ManageFrame(std::move(frame)); - } - void InsertH264(uint16_t seq_num_start, uint16_t seq_num_end, bool keyframe) { std::unique_ptr frame = CreateFrame(seq_num_start, seq_num_end, keyframe, kVideoCodecH264, @@ -151,11 +132,6 @@ class TestRtpFrameReferenceFinder : public ::testing::Test, CheckReferences(pid, 0, refs...); } - template - void CheckReferencesVp8(int64_t pid, T... refs) const { - CheckReferences(pid, 0, refs...); - } - template void CheckReferencesH264(int64_t pid, T... refs) const { CheckReferences(pid, 0, refs...); @@ -253,415 +229,6 @@ TEST_F(TestRtpFrameReferenceFinder, ClearTo) { EXPECT_EQ(3UL, frames_from_callback_.size()); } -TEST_F(TestRtpFrameReferenceFinder, Vp8NoPictureId) { - uint16_t sn = Rand(); - - InsertVp8(sn, sn + 2, true); - ASSERT_EQ(1UL, frames_from_callback_.size()); - - InsertVp8(sn + 3, sn + 4, false); - ASSERT_EQ(2UL, frames_from_callback_.size()); - - InsertVp8(sn + 5, sn + 8, false); - ASSERT_EQ(3UL, frames_from_callback_.size()); - - InsertVp8(sn + 9, sn + 9, false); - ASSERT_EQ(4UL, frames_from_callback_.size()); - - InsertVp8(sn + 10, sn + 11, false); - ASSERT_EQ(5UL, frames_from_callback_.size()); - - InsertVp8(sn + 12, sn + 12, true); - ASSERT_EQ(6UL, frames_from_callback_.size()); - - InsertVp8(sn + 13, sn + 17, false); - ASSERT_EQ(7UL, frames_from_callback_.size()); - - InsertVp8(sn + 18, sn + 18, false); - ASSERT_EQ(8UL, frames_from_callback_.size()); - - InsertVp8(sn + 19, sn + 20, false); - ASSERT_EQ(9UL, frames_from_callback_.size()); - - InsertVp8(sn + 21, sn + 21, false); - - ASSERT_EQ(10UL, frames_from_callback_.size()); - CheckReferencesVp8(sn + 2); - CheckReferencesVp8(sn + 4, sn + 2); - CheckReferencesVp8(sn + 8, sn + 4); - CheckReferencesVp8(sn + 9, sn + 8); - CheckReferencesVp8(sn + 11, sn + 9); - CheckReferencesVp8(sn + 12); - CheckReferencesVp8(sn + 17, sn + 12); - CheckReferencesVp8(sn + 18, sn + 17); - CheckReferencesVp8(sn + 20, sn + 18); - CheckReferencesVp8(sn + 21, sn + 20); -} - -TEST_F(TestRtpFrameReferenceFinder, Vp8NoPictureIdReordered) { - uint16_t sn = 0xfffa; - - InsertVp8(sn, sn + 2, true); - InsertVp8(sn + 3, sn + 4, false); - InsertVp8(sn + 5, sn + 8, false); - InsertVp8(sn + 9, sn + 9, false); - InsertVp8(sn + 10, sn + 11, false); - InsertVp8(sn + 12, sn + 12, true); - InsertVp8(sn + 13, sn + 17, false); - InsertVp8(sn + 18, sn + 18, false); - InsertVp8(sn + 19, sn + 20, false); - InsertVp8(sn + 21, sn + 21, false); - - ASSERT_EQ(10UL, frames_from_callback_.size()); - CheckReferencesVp8(sn + 2); - CheckReferencesVp8(sn + 4, sn + 2); - CheckReferencesVp8(sn + 8, sn + 4); - CheckReferencesVp8(sn + 9, sn + 8); - CheckReferencesVp8(sn + 11, sn + 9); - CheckReferencesVp8(sn + 12); - CheckReferencesVp8(sn + 17, sn + 12); - CheckReferencesVp8(sn + 18, sn + 17); - CheckReferencesVp8(sn + 20, sn + 18); - CheckReferencesVp8(sn + 21, sn + 20); -} - -TEST_F(TestRtpFrameReferenceFinder, Vp8KeyFrameReferences) { - uint16_t sn = Rand(); - InsertVp8(sn, sn, true); - - ASSERT_EQ(1UL, frames_from_callback_.size()); - CheckReferencesVp8(sn); -} - -TEST_F(TestRtpFrameReferenceFinder, Vp8RepeatedFrame_0) { - uint16_t pid = Rand(); - uint16_t sn = Rand(); - - InsertVp8(sn, sn, true, pid, 0, 1); - InsertVp8(sn + 1, sn + 1, false, pid + 1, 0, 2); - InsertVp8(sn + 1, sn + 1, false, pid + 1, 0, 2); - - ASSERT_EQ(2UL, frames_from_callback_.size()); - CheckReferencesVp8(pid); - CheckReferencesVp8(pid + 1, pid); -} - -TEST_F(TestRtpFrameReferenceFinder, Vp8RepeatedFrameLayerSync_01) { - uint16_t pid = Rand(); - uint16_t sn = Rand(); - - InsertVp8(sn, sn, true, pid, 0, 1); - InsertVp8(sn + 1, sn + 1, false, pid + 1, 1, 1, true); - ASSERT_EQ(2UL, frames_from_callback_.size()); - InsertVp8(sn + 1, sn + 1, false, pid + 1, 1, 1, true); - - ASSERT_EQ(2UL, frames_from_callback_.size()); - CheckReferencesVp8(pid); - CheckReferencesVp8(pid + 1, pid); -} - -TEST_F(TestRtpFrameReferenceFinder, Vp8RepeatedFrame_01) { - uint16_t pid = Rand(); - uint16_t sn = Rand(); - - InsertVp8(sn, sn, true, pid, 0, 1); - InsertVp8(sn + 1, sn + 1, false, pid + 1, 0, 2, true); - InsertVp8(sn + 2, sn + 2, false, pid + 2, 0, 3); - InsertVp8(sn + 3, sn + 3, false, pid + 3, 0, 4); - InsertVp8(sn + 3, sn + 3, false, pid + 3, 0, 4); - - ASSERT_EQ(4UL, frames_from_callback_.size()); - CheckReferencesVp8(pid); - CheckReferencesVp8(pid + 1, pid); - CheckReferencesVp8(pid + 2, pid + 1); - CheckReferencesVp8(pid + 3, pid + 2); -} - -// Test with 1 temporal layer. -TEST_F(TestRtpFrameReferenceFinder, Vp8TemporalLayers_0) { - uint16_t pid = Rand(); - uint16_t sn = Rand(); - - InsertVp8(sn, sn, true, pid, 0, 1); - InsertVp8(sn + 1, sn + 1, false, pid + 1, 0, 2); - InsertVp8(sn + 2, sn + 2, false, pid + 2, 0, 3); - InsertVp8(sn + 3, sn + 3, false, pid + 3, 0, 4); - - ASSERT_EQ(4UL, frames_from_callback_.size()); - CheckReferencesVp8(pid); - CheckReferencesVp8(pid + 1, pid); - CheckReferencesVp8(pid + 2, pid + 1); - CheckReferencesVp8(pid + 3, pid + 2); -} - -TEST_F(TestRtpFrameReferenceFinder, Vp8DuplicateTl1Frames) { - uint16_t pid = Rand(); - uint16_t sn = Rand(); - - InsertVp8(sn, sn, true, pid, 0, 0); - InsertVp8(sn + 1, sn + 1, false, pid + 1, 1, 0, true); - InsertVp8(sn + 2, sn + 2, false, pid + 2, 0, 1); - InsertVp8(sn + 3, sn + 3, false, pid + 3, 1, 1); - InsertVp8(sn + 3, sn + 3, false, pid + 3, 1, 1); - InsertVp8(sn + 4, sn + 4, false, pid + 4, 0, 2); - InsertVp8(sn + 5, sn + 5, false, pid + 5, 1, 2); - - ASSERT_EQ(6UL, frames_from_callback_.size()); - CheckReferencesVp8(pid); - CheckReferencesVp8(pid + 1, pid); - CheckReferencesVp8(pid + 2, pid); - CheckReferencesVp8(pid + 3, pid + 1, pid + 2); - CheckReferencesVp8(pid + 4, pid + 2); - CheckReferencesVp8(pid + 5, pid + 3, pid + 4); -} - -// Test with 1 temporal layer. -TEST_F(TestRtpFrameReferenceFinder, Vp8TemporalLayersReordering_0) { - uint16_t pid = Rand(); - uint16_t sn = Rand(); - - InsertVp8(sn, sn, true, pid, 0, 1); - InsertVp8(sn + 1, sn + 1, false, pid + 1, 0, 2); - InsertVp8(sn + 3, sn + 3, false, pid + 3, 0, 4); - InsertVp8(sn + 2, sn + 2, false, pid + 2, 0, 3); - InsertVp8(sn + 5, sn + 5, false, pid + 5, 0, 6); - InsertVp8(sn + 6, sn + 6, false, pid + 6, 0, 7); - InsertVp8(sn + 4, sn + 4, false, pid + 4, 0, 5); - - ASSERT_EQ(7UL, frames_from_callback_.size()); - CheckReferencesVp8(pid); - CheckReferencesVp8(pid + 1, pid); - CheckReferencesVp8(pid + 2, pid + 1); - CheckReferencesVp8(pid + 3, pid + 2); - CheckReferencesVp8(pid + 4, pid + 3); - CheckReferencesVp8(pid + 5, pid + 4); - CheckReferencesVp8(pid + 6, pid + 5); -} - -// Test with 2 temporal layers in a 01 pattern. -TEST_F(TestRtpFrameReferenceFinder, Vp8TemporalLayers_01) { - uint16_t pid = Rand(); - uint16_t sn = Rand(); - - InsertVp8(sn, sn, true, pid, 0, 255); - InsertVp8(sn + 1, sn + 1, false, pid + 1, 1, 255, true); - InsertVp8(sn + 2, sn + 2, false, pid + 2, 0, 0); - InsertVp8(sn + 3, sn + 3, false, pid + 3, 1, 0); - - ASSERT_EQ(4UL, frames_from_callback_.size()); - CheckReferencesVp8(pid); - CheckReferencesVp8(pid + 1, pid); - CheckReferencesVp8(pid + 2, pid); - CheckReferencesVp8(pid + 3, pid + 1, pid + 2); -} - -// Test with 2 temporal layers in a 01 pattern. -TEST_F(TestRtpFrameReferenceFinder, Vp8TemporalLayersReordering_01) { - uint16_t pid = Rand(); - uint16_t sn = Rand(); - - InsertVp8(sn + 1, sn + 1, false, pid + 1, 1, 255, true); - InsertVp8(sn, sn, true, pid, 0, 255); - InsertVp8(sn + 3, sn + 3, false, pid + 3, 1, 0); - InsertVp8(sn + 5, sn + 5, false, pid + 5, 1, 1); - InsertVp8(sn + 2, sn + 2, false, pid + 2, 0, 0); - InsertVp8(sn + 4, sn + 4, false, pid + 4, 0, 1); - InsertVp8(sn + 6, sn + 6, false, pid + 6, 0, 2); - InsertVp8(sn + 7, sn + 7, false, pid + 7, 1, 2); - - ASSERT_EQ(8UL, frames_from_callback_.size()); - CheckReferencesVp8(pid); - CheckReferencesVp8(pid + 1, pid); - CheckReferencesVp8(pid + 2, pid); - CheckReferencesVp8(pid + 3, pid + 1, pid + 2); - CheckReferencesVp8(pid + 4, pid + 2); - CheckReferencesVp8(pid + 5, pid + 3, pid + 4); - CheckReferencesVp8(pid + 6, pid + 4); - CheckReferencesVp8(pid + 7, pid + 5, pid + 6); -} - -// Test with 3 temporal layers in a 0212 pattern. -TEST_F(TestRtpFrameReferenceFinder, Vp8TemporalLayers_0212) { - uint16_t pid = Rand(); - uint16_t sn = Rand(); - - InsertVp8(sn, sn, true, pid, 0, 55); - InsertVp8(sn + 1, sn + 1, false, pid + 1, 2, 55, true); - InsertVp8(sn + 2, sn + 2, false, pid + 2, 1, 55, true); - InsertVp8(sn + 3, sn + 3, false, pid + 3, 2, 55); - InsertVp8(sn + 4, sn + 4, false, pid + 4, 0, 56); - InsertVp8(sn + 5, sn + 5, false, pid + 5, 2, 56); - InsertVp8(sn + 6, sn + 6, false, pid + 6, 1, 56); - InsertVp8(sn + 7, sn + 7, false, pid + 7, 2, 56); - InsertVp8(sn + 8, sn + 8, false, pid + 8, 0, 57); - InsertVp8(sn + 9, sn + 9, false, pid + 9, 2, 57, true); - InsertVp8(sn + 10, sn + 10, false, pid + 10, 1, 57, true); - InsertVp8(sn + 11, sn + 11, false, pid + 11, 2, 57); - - ASSERT_EQ(12UL, frames_from_callback_.size()); - CheckReferencesVp8(pid); - CheckReferencesVp8(pid + 1, pid); - CheckReferencesVp8(pid + 2, pid); - CheckReferencesVp8(pid + 3, pid, pid + 1, pid + 2); - CheckReferencesVp8(pid + 4, pid); - CheckReferencesVp8(pid + 5, pid + 2, pid + 3, pid + 4); - CheckReferencesVp8(pid + 6, pid + 2, pid + 4); - CheckReferencesVp8(pid + 7, pid + 4, pid + 5, pid + 6); - CheckReferencesVp8(pid + 8, pid + 4); - CheckReferencesVp8(pid + 9, pid + 8); - CheckReferencesVp8(pid + 10, pid + 8); - CheckReferencesVp8(pid + 11, pid + 8, pid + 9, pid + 10); -} - -// Test with 3 temporal layers in a 0212 pattern. -TEST_F(TestRtpFrameReferenceFinder, Vp8TemporalLayersMissingFrame_0212) { - uint16_t pid = Rand(); - uint16_t sn = Rand(); - - InsertVp8(sn, sn, true, pid, 0, 55, false); - InsertVp8(sn + 2, sn + 2, false, pid + 2, 1, 55, true); - InsertVp8(sn + 3, sn + 3, false, pid + 3, 2, 55, false); - - ASSERT_EQ(2UL, frames_from_callback_.size()); - CheckReferencesVp8(pid); - CheckReferencesVp8(pid + 2, pid); -} - -// Test with 3 temporal layers in a 0212 pattern. -TEST_F(TestRtpFrameReferenceFinder, Vp8TemporalLayersReordering_0212) { - uint16_t pid = 126; - uint16_t sn = Rand(); - - InsertVp8(sn + 1, sn + 1, false, pid + 1, 2, 55, true); - InsertVp8(sn, sn, true, pid, 0, 55, false); - InsertVp8(sn + 2, sn + 2, false, pid + 2, 1, 55, true); - InsertVp8(sn + 4, sn + 4, false, pid + 4, 0, 56, false); - InsertVp8(sn + 5, sn + 5, false, pid + 5, 2, 56, false); - InsertVp8(sn + 3, sn + 3, false, pid + 3, 2, 55, false); - InsertVp8(sn + 7, sn + 7, false, pid + 7, 2, 56, false); - InsertVp8(sn + 9, sn + 9, false, pid + 9, 2, 57, true); - InsertVp8(sn + 6, sn + 6, false, pid + 6, 1, 56, false); - InsertVp8(sn + 8, sn + 8, false, pid + 8, 0, 57, false); - InsertVp8(sn + 11, sn + 11, false, pid + 11, 2, 57, false); - InsertVp8(sn + 10, sn + 10, false, pid + 10, 1, 57, true); - - ASSERT_EQ(12UL, frames_from_callback_.size()); - CheckReferencesVp8(pid); - CheckReferencesVp8(pid + 1, pid); - CheckReferencesVp8(pid + 2, pid); - CheckReferencesVp8(pid + 3, pid, pid + 1, pid + 2); - CheckReferencesVp8(pid + 4, pid); - CheckReferencesVp8(pid + 5, pid + 2, pid + 3, pid + 4); - CheckReferencesVp8(pid + 6, pid + 2, pid + 4); - CheckReferencesVp8(pid + 7, pid + 4, pid + 5, pid + 6); - CheckReferencesVp8(pid + 8, pid + 4); - CheckReferencesVp8(pid + 9, pid + 8); - CheckReferencesVp8(pid + 10, pid + 8); - CheckReferencesVp8(pid + 11, pid + 8, pid + 9, pid + 10); -} - -TEST_F(TestRtpFrameReferenceFinder, Vp8InsertManyFrames_0212) { - uint16_t pid = Rand(); - uint16_t sn = Rand(); - - const int keyframes_to_insert = 50; - const int frames_per_keyframe = 120; // Should be a multiple of 4. - uint8_t tl0 = 128; - - for (int k = 0; k < keyframes_to_insert; ++k) { - InsertVp8(sn, sn, true, pid, 0, tl0, false); - InsertVp8(sn + 1, sn + 1, false, pid + 1, 2, tl0, true); - InsertVp8(sn + 2, sn + 2, false, pid + 2, 1, tl0, true); - InsertVp8(sn + 3, sn + 3, false, pid + 3, 2, tl0, false); - CheckReferencesVp8(pid); - CheckReferencesVp8(pid + 1, pid); - CheckReferencesVp8(pid + 2, pid); - CheckReferencesVp8(pid + 3, pid, pid + 1, pid + 2); - frames_from_callback_.clear(); - ++tl0; - - for (int f = 4; f < frames_per_keyframe; f += 4) { - uint16_t sf = sn + f; - int64_t pidf = pid + f; - - InsertVp8(sf, sf, false, pidf, 0, tl0, false); - InsertVp8(sf + 1, sf + 1, false, pidf + 1, 2, tl0, false); - InsertVp8(sf + 2, sf + 2, false, pidf + 2, 1, tl0, false); - InsertVp8(sf + 3, sf + 3, false, pidf + 3, 2, tl0, false); - CheckReferencesVp8(pidf, pidf - 4); - CheckReferencesVp8(pidf + 1, pidf, pidf - 1, pidf - 2); - CheckReferencesVp8(pidf + 2, pidf, pidf - 2); - CheckReferencesVp8(pidf + 3, pidf, pidf + 1, pidf + 2); - frames_from_callback_.clear(); - ++tl0; - } - - pid += frames_per_keyframe; - sn += frames_per_keyframe; - } -} - -TEST_F(TestRtpFrameReferenceFinder, Vp8LayerSync) { - uint16_t pid = Rand(); - uint16_t sn = Rand(); - - InsertVp8(sn, sn, true, pid, 0, 0, false); - InsertVp8(sn + 1, sn + 1, false, pid + 1, 1, 0, true); - InsertVp8(sn + 2, sn + 2, false, pid + 2, 0, 1, false); - ASSERT_EQ(3UL, frames_from_callback_.size()); - - InsertVp8(sn + 4, sn + 4, false, pid + 4, 0, 2, false); - InsertVp8(sn + 5, sn + 5, false, pid + 5, 1, 2, true); - InsertVp8(sn + 6, sn + 6, false, pid + 6, 0, 3, false); - InsertVp8(sn + 7, sn + 7, false, pid + 7, 1, 3, false); - - ASSERT_EQ(7UL, frames_from_callback_.size()); - CheckReferencesVp8(pid); - CheckReferencesVp8(pid + 1, pid); - CheckReferencesVp8(pid + 2, pid); - CheckReferencesVp8(pid + 4, pid + 2); - CheckReferencesVp8(pid + 5, pid + 4); - CheckReferencesVp8(pid + 6, pid + 4); - CheckReferencesVp8(pid + 7, pid + 6, pid + 5); -} - -TEST_F(TestRtpFrameReferenceFinder, Vp8Tl1SyncFrameAfterTl1Frame) { - InsertVp8(1000, 1000, true, 1, 0, 247, true); - InsertVp8(1001, 1001, false, 3, 0, 248, false); - InsertVp8(1002, 1002, false, 4, 1, 248, false); // Will be dropped - InsertVp8(1003, 1003, false, 5, 1, 248, true); // due to this frame. - - ASSERT_EQ(3UL, frames_from_callback_.size()); - CheckReferencesVp8(1); - CheckReferencesVp8(3, 1); - CheckReferencesVp8(5, 3); -} - -TEST_F(TestRtpFrameReferenceFinder, Vp8DetectMissingFrame_0212) { - InsertVp8(1, 1, true, 1, 0, 1, false); - InsertVp8(2, 2, false, 2, 2, 1, true); - InsertVp8(3, 3, false, 3, 1, 1, true); - InsertVp8(4, 4, false, 4, 2, 1, false); - - InsertVp8(6, 6, false, 6, 2, 2, false); - InsertVp8(7, 7, false, 7, 1, 2, false); - InsertVp8(8, 8, false, 8, 2, 2, false); - ASSERT_EQ(4UL, frames_from_callback_.size()); - - InsertVp8(5, 5, false, 5, 0, 2, false); - ASSERT_EQ(8UL, frames_from_callback_.size()); - - CheckReferencesVp8(1); - CheckReferencesVp8(2, 1); - CheckReferencesVp8(3, 1); - CheckReferencesVp8(4, 3, 2, 1); - - CheckReferencesVp8(5, 1); - CheckReferencesVp8(6, 5, 4, 3); - CheckReferencesVp8(7, 5, 3); - CheckReferencesVp8(8, 7, 6, 5); -} - TEST_F(TestRtpFrameReferenceFinder, H264KeyFrameReferences) { uint16_t sn = Rand(); InsertH264(sn, sn, true); diff --git a/modules/video_coding/rtp_vp8_ref_finder_unittest.cc b/modules/video_coding/rtp_vp8_ref_finder_unittest.cc new file mode 100644 index 0000000000..aa858807a3 --- /dev/null +++ b/modules/video_coding/rtp_vp8_ref_finder_unittest.cc @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2020 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/rtp_vp8_ref_finder.h" + +#include +#include + +#include "modules/video_coding/frame_object.h" +#include "test/gmock.h" +#include "test/gtest.h" + +using ::testing::Contains; +using ::testing::Eq; +using ::testing::Matcher; +using ::testing::Matches; +using ::testing::SizeIs; +using ::testing::UnorderedElementsAreArray; + +namespace webrtc { +namespace video_coding { +namespace { + +MATCHER_P2(HasIdAndRefs, id, refs, "") { + return Matches(Eq(id))(arg->Id()) && + Matches(UnorderedElementsAreArray(refs))( + rtc::ArrayView(arg->references, arg->num_references)); +} + +Matcher>&> +HasFrameWithIdAndRefs(int64_t frame_id, const std::vector& refs) { + return Contains(HasIdAndRefs(frame_id, refs)); +} + +class Frame { + public: + Frame& AsKeyFrame(bool is_keyframe = true) { + is_keyframe_ = is_keyframe; + return *this; + } + + Frame& Pid(int pid) { + picture_id_ = pid; + return *this; + } + + Frame& Tid(int tid) { + temporal_id_ = tid; + return *this; + } + + Frame& Tl0(int tl0) { + tl0_idx_ = tl0; + return *this; + } + + Frame& AsSync(bool is_sync = true) { + sync = is_sync; + return *this; + } + + operator std::unique_ptr() { + RTPVideoHeaderVP8 vp8_header{}; + vp8_header.pictureId = *picture_id_; + vp8_header.temporalIdx = *temporal_id_; + vp8_header.tl0PicIdx = *tl0_idx_; + vp8_header.layerSync = sync; + + RTPVideoHeader video_header; + video_header.frame_type = is_keyframe_ ? VideoFrameType::kVideoFrameKey + : VideoFrameType::kVideoFrameDelta; + video_header.video_type_header = vp8_header; + // clang-format off + return std::make_unique( + /*seq_num_start=*/0, + /*seq_num_end=*/0, + /*markerBit=*/true, + /*times_nacked=*/0, + /*first_packet_received_time=*/0, + /*last_packet_received_time=*/0, + /*rtp_timestamp=*/0, + /*ntp_time_ms=*/0, + VideoSendTiming(), + /*payload_type=*/0, + kVideoCodecVP8, + kVideoRotation_0, + VideoContentType::UNSPECIFIED, + video_header, + /*color_space=*/absl::nullopt, + RtpPacketInfos(), + EncodedImageBuffer::Create(/*size=*/0)); + // clang-format on + } + + private: + bool is_keyframe_ = false; + absl::optional picture_id_; + absl::optional temporal_id_; + absl::optional tl0_idx_; + bool sync = false; +}; + +} // namespace + +class RtpVp8RefFinderTest : public ::testing::Test { + protected: + RtpVp8RefFinderTest() : ref_finder_(std::make_unique()) {} + + void Insert(std::unique_ptr frame) { + for (auto& f : ref_finder_->ManageFrame(std::move(frame))) { + frames_.push_back(std::move(f)); + } + } + + std::unique_ptr ref_finder_; + std::vector> frames_; +}; + +TEST_F(RtpVp8RefFinderTest, Vp8RepeatedFrame_0) { + Insert(Frame().Pid(0).Tid(0).Tl0(1).AsKeyFrame()); + Insert(Frame().Pid(1).Tid(0).Tl0(2)); + Insert(Frame().Pid(1).Tid(0).Tl0(2)); + + EXPECT_THAT(frames_, SizeIs(2)); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(0, {})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(1, {0})); +} + +TEST_F(RtpVp8RefFinderTest, Vp8RepeatedFrameLayerSync_01) { + Insert(Frame().Pid(0).Tid(0).Tl0(1).AsKeyFrame()); + Insert(Frame().Pid(1).Tid(1).Tl0(1).AsSync()); + Insert(Frame().Pid(1).Tid(1).Tl0(1).AsSync()); + + EXPECT_THAT(frames_, SizeIs(2)); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(0, {})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(1, {0})); +} + +TEST_F(RtpVp8RefFinderTest, Vp8RepeatedFrame_01) { + Insert(Frame().Pid(0).Tid(0).Tl0(1).AsKeyFrame()); + Insert(Frame().Pid(1).Tid(0).Tl0(2).AsSync()); + Insert(Frame().Pid(2).Tid(0).Tl0(3)); + Insert(Frame().Pid(3).Tid(0).Tl0(4)); + Insert(Frame().Pid(3).Tid(0).Tl0(4)); + + EXPECT_THAT(frames_, SizeIs(4)); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(0, {})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(1, {0})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(2, {1})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(3, {2})); +} + +TEST_F(RtpVp8RefFinderTest, Vp8TemporalLayers_0) { + Insert(Frame().Pid(0).Tid(0).Tl0(1).AsKeyFrame()); + Insert(Frame().Pid(1).Tid(0).Tl0(2)); + + EXPECT_THAT(frames_, SizeIs(2)); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(0, {})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(1, {0})); +} + +TEST_F(RtpVp8RefFinderTest, Vp8DuplicateTl1Frames) { + Insert(Frame().Pid(0).Tid(0).Tl0(0).AsKeyFrame()); + Insert(Frame().Pid(1).Tid(1).Tl0(0).AsSync()); + Insert(Frame().Pid(2).Tid(0).Tl0(1)); + Insert(Frame().Pid(3).Tid(1).Tl0(1)); + Insert(Frame().Pid(3).Tid(1).Tl0(1)); + Insert(Frame().Pid(4).Tid(0).Tl0(2)); + Insert(Frame().Pid(5).Tid(1).Tl0(2)); + + EXPECT_THAT(frames_, SizeIs(6)); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(0, {})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(1, {0})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(2, {0})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(3, {1, 2})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(4, {2})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(5, {3, 4})); +} + +TEST_F(RtpVp8RefFinderTest, Vp8TemporalLayersReordering_0) { + Insert(Frame().Pid(1).Tid(0).Tl0(2)); + Insert(Frame().Pid(0).Tid(0).Tl0(1).AsKeyFrame()); + Insert(Frame().Pid(3).Tid(0).Tl0(4)); + Insert(Frame().Pid(2).Tid(0).Tl0(3)); + Insert(Frame().Pid(5).Tid(0).Tl0(6)); + Insert(Frame().Pid(6).Tid(0).Tl0(7)); + Insert(Frame().Pid(4).Tid(0).Tl0(5)); + + EXPECT_THAT(frames_, SizeIs(7)); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(0, {})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(1, {0})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(2, {1})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(3, {2})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(4, {3})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(5, {4})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(6, {5})); +} + +TEST_F(RtpVp8RefFinderTest, Vp8TemporalLayers_01) { + Insert(Frame().Pid(0).Tid(0).Tl0(255).AsKeyFrame()); + Insert(Frame().Pid(1).Tid(1).Tl0(255).AsSync()); + Insert(Frame().Pid(2).Tid(0).Tl0(0)); + Insert(Frame().Pid(3).Tid(1).Tl0(0)); + + EXPECT_THAT(frames_, SizeIs(4)); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(0, {})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(1, {0})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(2, {0})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(3, {1, 2})); +} + +TEST_F(RtpVp8RefFinderTest, Vp8TemporalLayersReordering_01) { + Insert(Frame().Pid(1).Tid(1).Tl0(255).AsSync()); + Insert(Frame().Pid(0).Tid(0).Tl0(255).AsKeyFrame()); + Insert(Frame().Pid(3).Tid(1).Tl0(0)); + Insert(Frame().Pid(5).Tid(1).Tl0(1)); + Insert(Frame().Pid(2).Tid(0).Tl0(0)); + Insert(Frame().Pid(4).Tid(0).Tl0(1)); + Insert(Frame().Pid(6).Tid(0).Tl0(2)); + Insert(Frame().Pid(7).Tid(1).Tl0(2)); + + EXPECT_THAT(frames_, SizeIs(8)); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(0, {})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(1, {0})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(2, {0})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(3, {1, 2})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(4, {2})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(5, {3, 4})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(6, {4})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(7, {5, 6})); +} + +TEST_F(RtpVp8RefFinderTest, Vp8TemporalLayers_0212) { + Insert(Frame().Pid(0).Tid(0).Tl0(55).AsKeyFrame()); + Insert(Frame().Pid(1).Tid(2).Tl0(55).AsSync()); + Insert(Frame().Pid(2).Tid(1).Tl0(55).AsSync()); + Insert(Frame().Pid(3).Tid(2).Tl0(55)); + Insert(Frame().Pid(4).Tid(0).Tl0(56)); + Insert(Frame().Pid(5).Tid(2).Tl0(56)); + Insert(Frame().Pid(6).Tid(1).Tl0(56)); + Insert(Frame().Pid(7).Tid(2).Tl0(56)); + Insert(Frame().Pid(8).Tid(0).Tl0(57)); + Insert(Frame().Pid(9).Tid(2).Tl0(57).AsSync()); + Insert(Frame().Pid(10).Tid(1).Tl0(57).AsSync()); + Insert(Frame().Pid(11).Tid(2).Tl0(57)); + + EXPECT_THAT(frames_, SizeIs(12)); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(0, {})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(1, {0})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(2, {0})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(3, {0, 1, 2})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(4, {0})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(5, {2, 3, 4})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(6, {2, 4})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(7, {4, 5, 6})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(8, {4})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(9, {8})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(10, {8})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(11, {8, 9, 10})); +} + +TEST_F(RtpVp8RefFinderTest, Vp8TemporalLayersMissingFrame_0212) { + Insert(Frame().Pid(0).Tid(0).Tl0(55).AsKeyFrame()); + Insert(Frame().Pid(2).Tid(1).Tl0(55).AsSync()); + Insert(Frame().Pid(3).Tid(2).Tl0(55)); + + EXPECT_THAT(frames_, SizeIs(2)); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(0, {})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(2, {0})); +} + +// Test with 3 temporal layers in a 0212 pattern. +TEST_F(RtpVp8RefFinderTest, Vp8TemporalLayersReordering_0212) { + Insert(Frame().Pid(127).Tid(2).Tl0(55).AsSync()); + Insert(Frame().Pid(126).Tid(0).Tl0(55).AsKeyFrame()); + Insert(Frame().Pid(128).Tid(1).Tl0(55).AsSync()); + Insert(Frame().Pid(130).Tid(0).Tl0(56)); + Insert(Frame().Pid(131).Tid(2).Tl0(56)); + Insert(Frame().Pid(129).Tid(2).Tl0(55)); + Insert(Frame().Pid(133).Tid(2).Tl0(56)); + Insert(Frame().Pid(135).Tid(2).Tl0(57).AsSync()); + Insert(Frame().Pid(132).Tid(1).Tl0(56)); + Insert(Frame().Pid(134).Tid(0).Tl0(57)); + Insert(Frame().Pid(137).Tid(2).Tl0(57)); + Insert(Frame().Pid(136).Tid(1).Tl0(57).AsSync()); + + EXPECT_THAT(frames_, SizeIs(12)); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(126, {})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(127, {126})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(128, {126})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(129, {126, 127, 128})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(130, {126})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(131, {128, 129, 130})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(132, {128, 130})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(133, {130, 131, 132})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(134, {130})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(135, {134})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(136, {134})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(137, {134, 135, 136})); +} + +TEST_F(RtpVp8RefFinderTest, Vp8LayerSync) { + Insert(Frame().Pid(0).Tid(0).Tl0(0).AsKeyFrame()); + Insert(Frame().Pid(1).Tid(1).Tl0(0).AsSync()); + Insert(Frame().Pid(2).Tid(0).Tl0(1)); + Insert(Frame().Pid(4).Tid(0).Tl0(2)); + Insert(Frame().Pid(5).Tid(1).Tl0(2).AsSync()); + Insert(Frame().Pid(6).Tid(0).Tl0(3)); + Insert(Frame().Pid(7).Tid(1).Tl0(3)); + + EXPECT_THAT(frames_, SizeIs(7)); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(0, {})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(1, {0})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(2, {0})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(4, {2})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(5, {4})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(6, {4})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(7, {5, 6})); +} + +TEST_F(RtpVp8RefFinderTest, Vp8Tl1SyncFrameAfterTl1Frame) { + Insert(Frame().Pid(1).Tid(0).Tl0(247).AsKeyFrame().AsSync()); + Insert(Frame().Pid(3).Tid(0).Tl0(248)); + Insert(Frame().Pid(4).Tid(1).Tl0(248)); + Insert(Frame().Pid(5).Tid(1).Tl0(248).AsSync()); + + EXPECT_THAT(frames_, SizeIs(3)); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(1, {})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(3, {1})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(5, {3})); +} + +TEST_F(RtpVp8RefFinderTest, Vp8DetectMissingFrame_0212) { + Insert(Frame().Pid(1).Tid(0).Tl0(1).AsKeyFrame()); + Insert(Frame().Pid(2).Tid(2).Tl0(1).AsSync()); + Insert(Frame().Pid(3).Tid(1).Tl0(1).AsSync()); + Insert(Frame().Pid(4).Tid(2).Tl0(1)); + Insert(Frame().Pid(6).Tid(2).Tl0(2)); + Insert(Frame().Pid(7).Tid(1).Tl0(2)); + Insert(Frame().Pid(8).Tid(2).Tl0(2)); + Insert(Frame().Pid(5).Tid(0).Tl0(2)); + + EXPECT_THAT(frames_, SizeIs(8)); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(1, {})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(2, {1})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(3, {1})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(4, {1, 2, 3})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(5, {1})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(6, {3, 4, 5})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(7, {3, 5})); + EXPECT_THAT(frames_, HasFrameWithIdAndRefs(8, {5, 6, 7})); +} + +} // namespace video_coding +} // namespace webrtc