Implement test utility for extracting changed part of video frames. Bug: webrtc:10152 Change-Id: Iead052d2a18384aaa828cd7821be961b8614568e Reviewed-on: https://webrtc-review.googlesource.com/c/120407 Commit-Queue: Ilya Nikolaevskiy <ilnik@webrtc.org> Reviewed-by: Erik Språng <sprang@webrtc.org> Cr-Commit-Position: refs/heads/master@{#26496}
159 lines
4.9 KiB
C++
159 lines
4.9 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 "test/frame_change_extractor.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include "api/video/i420_buffer.h"
|
|
#include "rtc_base/checks.h"
|
|
|
|
namespace webrtc {
|
|
|
|
FrameChangeExtractor::FrameChangeExtractor() = default;
|
|
FrameChangeExtractor::~FrameChangeExtractor() = default;
|
|
|
|
void FrameChangeExtractor::SetSource(
|
|
rtc::VideoSourceInterface<VideoFrame>* video_source) {
|
|
rtc::CritScope lock(&source_crit_);
|
|
|
|
rtc::VideoSourceInterface<VideoFrame>* old_source = nullptr;
|
|
old_source = source_;
|
|
source_ = video_source;
|
|
if (old_source != video_source && old_source != nullptr) {
|
|
old_source->RemoveSink(this);
|
|
}
|
|
if (video_source) {
|
|
source_->AddOrUpdateSink(this, rtc::VideoSinkWants());
|
|
}
|
|
}
|
|
|
|
void FrameChangeExtractor::OnFrame(const VideoFrame& frame) {
|
|
// Currently only supports I420 buffers.
|
|
RTC_CHECK(frame.video_frame_buffer()->type() ==
|
|
VideoFrameBuffer::Type::kI420);
|
|
|
|
rtc::CritScope lock(&sink_crit_);
|
|
|
|
if (!sink_)
|
|
return;
|
|
|
|
webrtc::I420BufferInterface* current_buffer =
|
|
frame.video_frame_buffer()->ToI420();
|
|
int min_row, min_col, max_row, max_col;
|
|
if (!last_frame_buffer_ ||
|
|
current_buffer->width() != last_frame_buffer_->width() ||
|
|
current_buffer->height() != last_frame_buffer_->height()) {
|
|
// New resolution - fully new picture.
|
|
min_row = min_col = 0;
|
|
max_row = current_buffer->height() - 1;
|
|
max_col = current_buffer->width() - 1;
|
|
} else {
|
|
min_row = frame.width();
|
|
min_col = frame.height();
|
|
max_row = 0;
|
|
max_col = 0;
|
|
// Detect changed rect.
|
|
for (int row = 0; row < frame.height(); ++row) {
|
|
for (int col = 0; col < frame.width(); ++col) {
|
|
int uv_row = row / 2;
|
|
int uv_col = col / 2;
|
|
if (current_buffer
|
|
->DataU()[uv_row * current_buffer->StrideU() + uv_col] !=
|
|
last_frame_buffer_
|
|
->DataU()[uv_row * last_frame_buffer_->StrideU() +
|
|
uv_col] ||
|
|
current_buffer
|
|
->DataV()[uv_row * current_buffer->StrideV() + uv_col] !=
|
|
last_frame_buffer_
|
|
->DataV()[uv_row * last_frame_buffer_->StrideV() +
|
|
uv_col] ||
|
|
current_buffer->DataY()[row * current_buffer->StrideY() + col] !=
|
|
last_frame_buffer_
|
|
->DataY()[row * last_frame_buffer_->StrideY() + col]) {
|
|
min_row = std::min(min_row, row);
|
|
max_row = std::max(max_row, row);
|
|
min_col = std::min(min_col, col);
|
|
max_col = std::max(max_col, col);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (max_row < min_row || max_col < min_col) {
|
|
min_col = min_row = 0;
|
|
max_col = max_row = -1;
|
|
}
|
|
// Expand changed rect to accommodate subsampled UV plane.
|
|
if (min_row % 2)
|
|
--min_row;
|
|
if (min_col % 2)
|
|
--min_row;
|
|
if (max_row < frame.width() && max_row % 2 == 0)
|
|
++max_row;
|
|
if (max_col < frame.height() && max_col % 2 == 0)
|
|
++max_col;
|
|
VideoFrame::PartialFrameDescription part_desc;
|
|
part_desc.offset_x = min_col;
|
|
part_desc.offset_y = min_row;
|
|
int changed_width = max_col - min_col + 1;
|
|
int changed_height = max_row - min_row + 1;
|
|
last_frame_buffer_ = current_buffer;
|
|
VideoFrame copy = frame;
|
|
if (changed_width > 0 && changed_height > 0) {
|
|
rtc::scoped_refptr<I420Buffer> changed_buffer =
|
|
I420Buffer::Create(changed_width, changed_height);
|
|
changed_buffer->CropAndScaleFrom(*current_buffer, part_desc.offset_x,
|
|
part_desc.offset_y, changed_width,
|
|
changed_height);
|
|
copy.set_video_frame_buffer(changed_buffer);
|
|
} else {
|
|
copy.set_video_frame_buffer(nullptr);
|
|
}
|
|
if (changed_width < frame.width() || changed_height < frame.height()) {
|
|
copy.set_partial_frame_description(part_desc);
|
|
}
|
|
copy.set_cache_buffer_for_partial_updates(true);
|
|
sink_->OnFrame(copy);
|
|
}
|
|
|
|
void FrameChangeExtractor::AddOrUpdateSink(
|
|
rtc::VideoSinkInterface<VideoFrame>* sink,
|
|
const rtc::VideoSinkWants& wants) {
|
|
RTC_CHECK(wants.partial_frames);
|
|
{
|
|
rtc::CritScope lock(&source_crit_);
|
|
if (source_) {
|
|
source_->AddOrUpdateSink(this, wants);
|
|
}
|
|
}
|
|
{
|
|
rtc::CritScope lock(&sink_crit_);
|
|
// Several sinks are unsupported.
|
|
RTC_CHECK(sink_ == nullptr || sink_ == sink);
|
|
sink_ = sink;
|
|
}
|
|
}
|
|
|
|
void FrameChangeExtractor::RemoveSink(
|
|
rtc::VideoSinkInterface<VideoFrame>* sink) {
|
|
{
|
|
rtc::CritScope lock(&source_crit_);
|
|
if (source_) {
|
|
source_->RemoveSink(this);
|
|
}
|
|
}
|
|
{
|
|
rtc::CritScope lock(&sink_crit_);
|
|
sink_ = nullptr;
|
|
}
|
|
}
|
|
|
|
} // namespace webrtc
|