189 lines
6.4 KiB
C++
189 lines
6.4 KiB
C++
/*
|
|
* Copyright 2018 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/scenario/video_frame_matcher.h"
|
|
|
|
#include <utility>
|
|
|
|
#include "common_video/libyuv/include/webrtc_libyuv.h"
|
|
#include "rtc_base/checks.h"
|
|
#include "rtc_base/event.h"
|
|
|
|
namespace webrtc {
|
|
namespace test {
|
|
namespace {
|
|
constexpr int kThumbWidth = 96;
|
|
constexpr int kThumbHeight = 96;
|
|
} // namespace
|
|
|
|
VideoFrameMatcher::VideoFrameMatcher(
|
|
std::vector<std::function<void(const VideoFramePair&)> >
|
|
frame_pair_handlers)
|
|
: frame_pair_handlers_(std::move(frame_pair_handlers)),
|
|
task_queue_("VideoAnalyzer") {}
|
|
|
|
VideoFrameMatcher::~VideoFrameMatcher() {
|
|
task_queue_.SendTask([this] { Finalize(); }, RTC_FROM_HERE);
|
|
}
|
|
|
|
void VideoFrameMatcher::RegisterLayer(int layer_id) {
|
|
task_queue_.PostTask([this, layer_id] { layers_[layer_id] = VideoLayer(); });
|
|
}
|
|
|
|
void VideoFrameMatcher::OnCapturedFrame(const VideoFrame& frame,
|
|
Timestamp at_time) {
|
|
CapturedFrame captured;
|
|
captured.id = next_capture_id_++;
|
|
captured.capture_time = at_time;
|
|
captured.frame = frame.video_frame_buffer();
|
|
captured.thumb = ScaleVideoFrameBuffer(*frame.video_frame_buffer()->ToI420(),
|
|
kThumbWidth, kThumbHeight),
|
|
task_queue_.PostTask([this, captured]() {
|
|
for (auto& layer : layers_) {
|
|
CapturedFrame copy = captured;
|
|
if (layer.second.last_decode &&
|
|
layer.second.last_decode->frame->width() <= captured.frame->width()) {
|
|
copy.best_score = I420SSE(*captured.thumb->GetI420(),
|
|
*layer.second.last_decode->thumb->GetI420());
|
|
copy.best_decode = layer.second.last_decode;
|
|
}
|
|
layer.second.captured_frames.push_back(std::move(copy));
|
|
}
|
|
});
|
|
}
|
|
|
|
void VideoFrameMatcher::OnDecodedFrame(const VideoFrame& frame,
|
|
int layer_id,
|
|
Timestamp render_time,
|
|
Timestamp at_time) {
|
|
rtc::scoped_refptr<DecodedFrame> decoded(new DecodedFrame{});
|
|
decoded->decoded_time = at_time;
|
|
decoded->render_time = render_time;
|
|
decoded->frame = frame.video_frame_buffer();
|
|
decoded->thumb = ScaleVideoFrameBuffer(*frame.video_frame_buffer()->ToI420(),
|
|
kThumbWidth, kThumbHeight);
|
|
|
|
task_queue_.PostTask([this, decoded, layer_id] {
|
|
auto& layer = layers_[layer_id];
|
|
decoded->id = layer.next_decoded_id++;
|
|
layer.last_decode = decoded;
|
|
for (auto& captured : layer.captured_frames) {
|
|
// We can't match with a smaller capture.
|
|
if (captured.frame->width() < decoded->frame->width()) {
|
|
captured.matched = true;
|
|
continue;
|
|
}
|
|
double score =
|
|
I420SSE(*captured.thumb->GetI420(), *decoded->thumb->GetI420());
|
|
if (score < captured.best_score) {
|
|
captured.best_score = score;
|
|
captured.best_decode = decoded;
|
|
captured.matched = false;
|
|
} else {
|
|
captured.matched = true;
|
|
}
|
|
}
|
|
while (!layer.captured_frames.empty() &&
|
|
layer.captured_frames.front().matched) {
|
|
HandleMatch(std::move(layer.captured_frames.front()), layer_id);
|
|
layer.captured_frames.pop_front();
|
|
}
|
|
});
|
|
}
|
|
|
|
bool VideoFrameMatcher::Active() const {
|
|
return !frame_pair_handlers_.empty();
|
|
}
|
|
|
|
void VideoFrameMatcher::HandleMatch(VideoFrameMatcher::CapturedFrame captured,
|
|
int layer_id) {
|
|
VideoFramePair frame_pair;
|
|
frame_pair.layer_id = layer_id;
|
|
frame_pair.captured = captured.frame;
|
|
frame_pair.capture_id = captured.id;
|
|
frame_pair.capture_time = captured.capture_time;
|
|
if (captured.best_decode) {
|
|
frame_pair.decode_id = captured.best_decode->id;
|
|
frame_pair.decoded = captured.best_decode->frame;
|
|
frame_pair.decoded_time = captured.best_decode->decoded_time;
|
|
// We can't render frames before they have been decoded.
|
|
frame_pair.render_time = std::max(captured.best_decode->render_time,
|
|
captured.best_decode->decoded_time);
|
|
frame_pair.repeated = captured.best_decode->repeat_count++;
|
|
}
|
|
for (auto& handler : frame_pair_handlers_)
|
|
handler(frame_pair);
|
|
}
|
|
|
|
void VideoFrameMatcher::Finalize() {
|
|
for (auto& layer : layers_) {
|
|
while (!layer.second.captured_frames.empty()) {
|
|
HandleMatch(std::move(layer.second.captured_frames.front()), layer.first);
|
|
layer.second.captured_frames.pop_front();
|
|
}
|
|
}
|
|
}
|
|
|
|
CapturedFrameTap::CapturedFrameTap(Clock* clock, VideoFrameMatcher* matcher)
|
|
: clock_(clock), matcher_(matcher) {}
|
|
|
|
void CapturedFrameTap::OnFrame(const VideoFrame& frame) {
|
|
matcher_->OnCapturedFrame(frame, clock_->CurrentTime());
|
|
}
|
|
void CapturedFrameTap::OnDiscardedFrame() {
|
|
discarded_count_++;
|
|
}
|
|
|
|
ForwardingCapturedFrameTap::ForwardingCapturedFrameTap(
|
|
Clock* clock,
|
|
VideoFrameMatcher* matcher,
|
|
rtc::VideoSourceInterface<VideoFrame>* source)
|
|
: clock_(clock), matcher_(matcher), source_(source) {}
|
|
|
|
void ForwardingCapturedFrameTap::OnFrame(const VideoFrame& frame) {
|
|
RTC_CHECK(sink_);
|
|
matcher_->OnCapturedFrame(frame, clock_->CurrentTime());
|
|
sink_->OnFrame(frame);
|
|
}
|
|
void ForwardingCapturedFrameTap::OnDiscardedFrame() {
|
|
RTC_CHECK(sink_);
|
|
discarded_count_++;
|
|
sink_->OnDiscardedFrame();
|
|
}
|
|
|
|
void ForwardingCapturedFrameTap::AddOrUpdateSink(
|
|
VideoSinkInterface<VideoFrame>* sink,
|
|
const rtc::VideoSinkWants& wants) {
|
|
if (!sink_)
|
|
sink_ = sink;
|
|
RTC_DCHECK_EQ(sink_, sink);
|
|
source_->AddOrUpdateSink(this, wants);
|
|
}
|
|
void ForwardingCapturedFrameTap::RemoveSink(
|
|
VideoSinkInterface<VideoFrame>* sink) {
|
|
source_->RemoveSink(this);
|
|
sink_ = nullptr;
|
|
}
|
|
|
|
DecodedFrameTap::DecodedFrameTap(Clock* clock,
|
|
VideoFrameMatcher* matcher,
|
|
int layer_id)
|
|
: clock_(clock), matcher_(matcher), layer_id_(layer_id) {
|
|
matcher_->RegisterLayer(layer_id_);
|
|
}
|
|
|
|
void DecodedFrameTap::OnFrame(const VideoFrame& frame) {
|
|
matcher_->OnDecodedFrame(frame, layer_id_,
|
|
Timestamp::Millis(frame.render_time_ms()),
|
|
clock_->CurrentTime());
|
|
}
|
|
|
|
} // namespace test
|
|
} // namespace webrtc
|