Implement utility for applying partial updates to video frames. Bug: webrtc:10152 Change-Id: I295fa9f792b96bbf1140a13f1f04e4f9deaccd5c Reviewed-on: https://webrtc-review.googlesource.com/c/120408 Commit-Queue: Ilya Nikolaevskiy <ilnik@webrtc.org> Reviewed-by: Niels Moller <nisse@webrtc.org> Reviewed-by: Erik Språng <sprang@webrtc.org> Cr-Commit-Position: refs/heads/master@{#26522}
280 lines
9.2 KiB
C++
280 lines
9.2 KiB
C++
/*
|
|
* 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<I420Buffer> CreatePicture(int width,
|
|
int height,
|
|
uint8_t data) {
|
|
rtc::scoped_refptr<I420Buffer> 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<I420Buffer> buf) {
|
|
VideoFrame frame = VideoFrame(buf, VideoRotation::kVideoRotation_0, 0);
|
|
frame.set_cache_buffer_for_partial_updates(true);
|
|
return frame;
|
|
}
|
|
|
|
bool TestPictureWithOneRect(rtc::scoped_refptr<I420BufferInterface> 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
|