/* * Copyright (c) 2019 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 "video/partial_frame_assembler.h" #include "api/video/i420_buffer.h" #include "api/video/video_frame.h" #include "test/gmock.h" #include "test/gtest.h" namespace webrtc { namespace { constexpr uint8_t kCol1 = 100; constexpr uint8_t kCol2 = 200; constexpr int kWidth = 640; constexpr int kHeight = 480; rtc::scoped_refptr CreatePicture(int width, int height, uint8_t data) { rtc::scoped_refptr buf = I420Buffer::Create(width, height); for (int row = 0; row < height; ++row) { for (int col = 0; col < width; ++col) { int pos_y = row * buf->StrideY() + col; int pos_u = row / 2 * buf->StrideU() + col / 2; int pos_v = row / 2 * buf->StrideV() + col / 2; buf->MutableDataY()[pos_y] = data; buf->MutableDataU()[pos_u] = data; buf->MutableDataV()[pos_v] = data; } } return buf; } VideoFrame CreateFrame(rtc::scoped_refptr buf) { VideoFrame frame = VideoFrame(buf, VideoRotation::kVideoRotation_0, 0); frame.set_cache_buffer_for_partial_updates(true); return frame; } bool TestPictureWithOneRect(rtc::scoped_refptr buf, int offset_x, int offset_y, int rect_width, int rect_height, uint8_t in_rect_data, uint8_t out_rect_data) { for (int row = 0; row < buf->height(); ++row) { for (int col = 0; col < buf->width(); ++col) { int pos_y = row * buf->StrideY() + col; int pos_u = row / 2 * buf->StrideU() + col / 2; int pos_v = row / 2 * buf->StrideV() + col / 2; uint8_t y = buf->DataY()[pos_y]; uint8_t u = buf->DataU()[pos_u]; uint8_t v = buf->DataV()[pos_v]; bool in_rect = col >= offset_x && col < offset_x + rect_width && row >= offset_y && row < offset_y + rect_height; uint8_t expected_data = in_rect ? in_rect_data : out_rect_data; if (y != expected_data || u != expected_data || v != expected_data) return false; } } return true; } TEST(PartialFrameAssembler, FullPictureUpdates) { PartialFrameAssembler decompressor; // Full pic. auto full_pic1 = CreatePicture(kWidth, kHeight, kCol1); VideoFrame frame1 = CreateFrame(full_pic1); // Full pic. auto full_pic2 = CreatePicture(kWidth, kHeight, kCol2); VideoFrame frame2 = CreateFrame(full_pic2); EXPECT_TRUE( decompressor.ApplyPartialUpdate(full_pic1.get(), &frame1, nullptr)); EXPECT_TRUE(TestPictureWithOneRect(frame1.video_frame_buffer()->ToI420(), 0, 0, -1, -1, kCol1, kCol1)); EXPECT_TRUE( decompressor.ApplyPartialUpdate(full_pic2.get(), &frame2, nullptr)); EXPECT_TRUE(TestPictureWithOneRect(frame2.video_frame_buffer()->ToI420(), 0, 0, -1, -1, kCol2, kCol2)); } TEST(PartialFrameAssembler, PartialUpdateFirstFails) { PartialFrameAssembler decompressor; // Partial update. auto full_pic1 = CreatePicture(20, 20, kCol1); VideoFrame::PartialFrameDescription desc1{0, 0}; VideoFrame frame1 = CreateFrame(full_pic1); EXPECT_FALSE( decompressor.ApplyPartialUpdate(full_pic1.get(), &frame1, &desc1)); } TEST(PartialFrameAssembler, PartialUpdate) { PartialFrameAssembler decompressor; // Full pic. auto full_pic1 = CreatePicture(kWidth, kHeight, kCol1); VideoFrame frame1 = CreateFrame(full_pic1); // Partial pic update. auto full_pic2 = CreatePicture(10, 20, kCol2); VideoFrame::PartialFrameDescription desc2{30, 40}; VideoFrame frame2 = CreateFrame(full_pic2); EXPECT_TRUE( decompressor.ApplyPartialUpdate(full_pic1.get(), &frame1, nullptr)); EXPECT_TRUE(TestPictureWithOneRect(frame1.video_frame_buffer()->ToI420(), 0, 0, -1, -1, kCol1, kCol1)); EXPECT_TRUE( decompressor.ApplyPartialUpdate(full_pic2.get(), &frame2, &desc2)); EXPECT_TRUE(TestPictureWithOneRect(frame2.video_frame_buffer()->ToI420(), 30, 40, 10, 20, kCol2, kCol1)); } TEST(PartialFrameAssembler, ProcessesUnchangedUpdate) { PartialFrameAssembler decompressor; auto full_pic1 = CreatePicture(kWidth, kHeight, kCol1); VideoFrame frame1 = CreateFrame(full_pic1); // Partial pic update. auto full_pic2 = CreatePicture(10, 20, kCol2); VideoFrame::PartialFrameDescription desc2{30, 40}; VideoFrame frame2 = CreateFrame(full_pic2); // Empty update. VideoFrame::PartialFrameDescription desc3{0, 0}; VideoFrame frame3 = CreateFrame(nullptr); EXPECT_TRUE( decompressor.ApplyPartialUpdate(full_pic1.get(), &frame1, nullptr)); EXPECT_TRUE( decompressor.ApplyPartialUpdate(full_pic2.get(), &frame2, &desc2)); EXPECT_TRUE(decompressor.ApplyPartialUpdate(nullptr, &frame3, &desc3)); } TEST(PartialFrameAssembler, PartialUpdateFailsForOddXOffset) { PartialFrameAssembler decompressor; // Full pic. auto full_pic1 = CreatePicture(kWidth, kHeight, kCol1); VideoFrame frame1 = CreateFrame(full_pic1); // Partial pic update. auto full_pic2 = CreatePicture(10, 20, kCol2); // Offset is odd. VideoFrame::PartialFrameDescription desc2{31, 40}; VideoFrame frame2 = CreateFrame(full_pic2); EXPECT_TRUE( decompressor.ApplyPartialUpdate(full_pic1.get(), &frame1, nullptr)); EXPECT_FALSE( decompressor.ApplyPartialUpdate(full_pic2.get(), &frame2, &desc2)); } TEST(PartialFrameAssembler, PartialUpdateFailsForOddYOffset) { PartialFrameAssembler decompressor; // Full pic. auto full_pic1 = CreatePicture(kWidth, kHeight, kCol1); VideoFrame frame1 = CreateFrame(full_pic1); // Partial pic update. auto full_pic2 = CreatePicture(10, 20, kCol2); // Offset is odd. VideoFrame::PartialFrameDescription desc2{30, 41}; VideoFrame frame2 = CreateFrame(full_pic2); EXPECT_TRUE( decompressor.ApplyPartialUpdate(full_pic1.get(), &frame1, nullptr)); EXPECT_FALSE( decompressor.ApplyPartialUpdate(full_pic2.get(), &frame2, &desc2)); } TEST(PartialFrameAssembler, PartialUpdateFailsForOddWidth) { PartialFrameAssembler decompressor; // Full pic. auto full_pic1 = CreatePicture(kWidth, kHeight, kCol1); VideoFrame frame1 = CreateFrame(full_pic1); // Partial pic update. Odd width. auto full_pic2 = CreatePicture(11, 20, kCol2); VideoFrame::PartialFrameDescription desc2{30, 40}; VideoFrame frame2 = CreateFrame(full_pic2); EXPECT_TRUE( decompressor.ApplyPartialUpdate(full_pic1.get(), &frame1, nullptr)); EXPECT_FALSE( decompressor.ApplyPartialUpdate(full_pic2.get(), &frame2, &desc2)); } TEST(PartialFrameAssembler, PartialUpdateWorksForOddWidthAtTheEnd) { PartialFrameAssembler decompressor; // Full pic. Odd resolution. auto full_pic1 = CreatePicture(kWidth + 1, kHeight + 1, kCol1); VideoFrame frame1 = CreateFrame(full_pic1); // Partial pic update. Odd width. auto full_pic2 = CreatePicture(11, 11, kCol2); VideoFrame::PartialFrameDescription desc2{kWidth + 1 - 11, kHeight + 1 - 11}; VideoFrame frame2 = CreateFrame(full_pic2); EXPECT_TRUE( decompressor.ApplyPartialUpdate(full_pic1.get(), &frame1, nullptr)); EXPECT_TRUE( decompressor.ApplyPartialUpdate(full_pic2.get(), &frame2, &desc2)); } TEST(PartialFrameAssembler, PartialUpdateFailsForOddNotAtEnd) { PartialFrameAssembler decompressor; // Full pic. Odd resolution. auto full_pic1 = CreatePicture(kWidth + 1, kHeight + 1, kCol1); VideoFrame frame1 = CreateFrame(full_pic1); // Partial pic update. Odd width. auto full_pic2 = CreatePicture(11, 11, kCol2); VideoFrame::PartialFrameDescription desc2{kWidth + 1 - 11, kHeight + 1 - 20}; VideoFrame frame2 = CreateFrame(full_pic2); EXPECT_TRUE( decompressor.ApplyPartialUpdate(full_pic1.get(), &frame1, nullptr)); EXPECT_FALSE( decompressor.ApplyPartialUpdate(full_pic2.get(), &frame2, &desc2)); } TEST(PartialFrameAssembler, FullPictureUpdatesCanChangeResolution) { PartialFrameAssembler decompressor; // Full pic. auto full_pic1 = CreatePicture(kWidth, kHeight, kCol1); VideoFrame frame1 = CreateFrame(full_pic1); // Full pic. auto full_pic2 = CreatePicture(kWidth + 100, kHeight + 100, kCol2); VideoFrame frame2 = CreateFrame(full_pic2); EXPECT_TRUE( decompressor.ApplyPartialUpdate(full_pic1.get(), &frame1, nullptr)); EXPECT_TRUE(TestPictureWithOneRect(frame1.video_frame_buffer()->ToI420(), 0, 0, -1, -1, kCol1, kCol1)); EXPECT_EQ(frame1.video_frame_buffer()->width(), kWidth); EXPECT_EQ(frame1.video_frame_buffer()->height(), kHeight); EXPECT_TRUE( decompressor.ApplyPartialUpdate(full_pic2.get(), &frame2, nullptr)); EXPECT_EQ(frame2.video_frame_buffer()->width(), kWidth + 100); EXPECT_EQ(frame2.video_frame_buffer()->height(), kHeight + 100); } } // namespace } // namespace webrtc