1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker * Copyright 2018 The WebRTC project authors. All Rights Reserved.
3*d9f75844SAndroid Build Coastguard Worker *
4*d9f75844SAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker * that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker * tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker * in the file PATENTS. All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker * be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker */
10*d9f75844SAndroid Build Coastguard Worker #include "test/scenario/video_frame_matcher.h"
11*d9f75844SAndroid Build Coastguard Worker
12*d9f75844SAndroid Build Coastguard Worker #include <utility>
13*d9f75844SAndroid Build Coastguard Worker
14*d9f75844SAndroid Build Coastguard Worker #include "common_video/libyuv/include/webrtc_libyuv.h"
15*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/checks.h"
16*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/event.h"
17*d9f75844SAndroid Build Coastguard Worker
18*d9f75844SAndroid Build Coastguard Worker namespace webrtc {
19*d9f75844SAndroid Build Coastguard Worker namespace test {
20*d9f75844SAndroid Build Coastguard Worker namespace {
21*d9f75844SAndroid Build Coastguard Worker constexpr int kThumbWidth = 96;
22*d9f75844SAndroid Build Coastguard Worker constexpr int kThumbHeight = 96;
23*d9f75844SAndroid Build Coastguard Worker } // namespace
24*d9f75844SAndroid Build Coastguard Worker
VideoFrameMatcher(std::vector<std::function<void (const VideoFramePair &)>> frame_pair_handlers)25*d9f75844SAndroid Build Coastguard Worker VideoFrameMatcher::VideoFrameMatcher(
26*d9f75844SAndroid Build Coastguard Worker std::vector<std::function<void(const VideoFramePair&)> >
27*d9f75844SAndroid Build Coastguard Worker frame_pair_handlers)
28*d9f75844SAndroid Build Coastguard Worker : frame_pair_handlers_(std::move(frame_pair_handlers)),
29*d9f75844SAndroid Build Coastguard Worker task_queue_("VideoAnalyzer") {}
30*d9f75844SAndroid Build Coastguard Worker
~VideoFrameMatcher()31*d9f75844SAndroid Build Coastguard Worker VideoFrameMatcher::~VideoFrameMatcher() {
32*d9f75844SAndroid Build Coastguard Worker task_queue_.SendTask([this] { Finalize(); });
33*d9f75844SAndroid Build Coastguard Worker }
34*d9f75844SAndroid Build Coastguard Worker
RegisterLayer(int layer_id)35*d9f75844SAndroid Build Coastguard Worker void VideoFrameMatcher::RegisterLayer(int layer_id) {
36*d9f75844SAndroid Build Coastguard Worker task_queue_.PostTask([this, layer_id] { layers_[layer_id] = VideoLayer(); });
37*d9f75844SAndroid Build Coastguard Worker }
38*d9f75844SAndroid Build Coastguard Worker
OnCapturedFrame(const VideoFrame & frame,Timestamp at_time)39*d9f75844SAndroid Build Coastguard Worker void VideoFrameMatcher::OnCapturedFrame(const VideoFrame& frame,
40*d9f75844SAndroid Build Coastguard Worker Timestamp at_time) {
41*d9f75844SAndroid Build Coastguard Worker CapturedFrame captured;
42*d9f75844SAndroid Build Coastguard Worker captured.id = next_capture_id_++;
43*d9f75844SAndroid Build Coastguard Worker captured.capture_time = at_time;
44*d9f75844SAndroid Build Coastguard Worker captured.frame = frame.video_frame_buffer();
45*d9f75844SAndroid Build Coastguard Worker captured.thumb = ScaleVideoFrameBuffer(*frame.video_frame_buffer()->ToI420(),
46*d9f75844SAndroid Build Coastguard Worker kThumbWidth, kThumbHeight),
47*d9f75844SAndroid Build Coastguard Worker task_queue_.PostTask([this, captured]() {
48*d9f75844SAndroid Build Coastguard Worker for (auto& layer : layers_) {
49*d9f75844SAndroid Build Coastguard Worker CapturedFrame copy = captured;
50*d9f75844SAndroid Build Coastguard Worker if (layer.second.last_decode &&
51*d9f75844SAndroid Build Coastguard Worker layer.second.last_decode->frame->width() <= captured.frame->width()) {
52*d9f75844SAndroid Build Coastguard Worker copy.best_score = I420SSE(*captured.thumb->GetI420(),
53*d9f75844SAndroid Build Coastguard Worker *layer.second.last_decode->thumb->GetI420());
54*d9f75844SAndroid Build Coastguard Worker copy.best_decode = layer.second.last_decode;
55*d9f75844SAndroid Build Coastguard Worker }
56*d9f75844SAndroid Build Coastguard Worker layer.second.captured_frames.push_back(std::move(copy));
57*d9f75844SAndroid Build Coastguard Worker }
58*d9f75844SAndroid Build Coastguard Worker });
59*d9f75844SAndroid Build Coastguard Worker }
60*d9f75844SAndroid Build Coastguard Worker
OnDecodedFrame(const VideoFrame & frame,int layer_id,Timestamp render_time,Timestamp at_time)61*d9f75844SAndroid Build Coastguard Worker void VideoFrameMatcher::OnDecodedFrame(const VideoFrame& frame,
62*d9f75844SAndroid Build Coastguard Worker int layer_id,
63*d9f75844SAndroid Build Coastguard Worker Timestamp render_time,
64*d9f75844SAndroid Build Coastguard Worker Timestamp at_time) {
65*d9f75844SAndroid Build Coastguard Worker rtc::scoped_refptr<DecodedFrame> decoded(new DecodedFrame{});
66*d9f75844SAndroid Build Coastguard Worker decoded->decoded_time = at_time;
67*d9f75844SAndroid Build Coastguard Worker decoded->render_time = render_time;
68*d9f75844SAndroid Build Coastguard Worker decoded->frame = frame.video_frame_buffer();
69*d9f75844SAndroid Build Coastguard Worker decoded->thumb = ScaleVideoFrameBuffer(*frame.video_frame_buffer()->ToI420(),
70*d9f75844SAndroid Build Coastguard Worker kThumbWidth, kThumbHeight);
71*d9f75844SAndroid Build Coastguard Worker
72*d9f75844SAndroid Build Coastguard Worker task_queue_.PostTask([this, decoded, layer_id] {
73*d9f75844SAndroid Build Coastguard Worker auto& layer = layers_[layer_id];
74*d9f75844SAndroid Build Coastguard Worker decoded->id = layer.next_decoded_id++;
75*d9f75844SAndroid Build Coastguard Worker layer.last_decode = decoded;
76*d9f75844SAndroid Build Coastguard Worker for (auto& captured : layer.captured_frames) {
77*d9f75844SAndroid Build Coastguard Worker // We can't match with a smaller capture.
78*d9f75844SAndroid Build Coastguard Worker if (captured.frame->width() < decoded->frame->width()) {
79*d9f75844SAndroid Build Coastguard Worker captured.matched = true;
80*d9f75844SAndroid Build Coastguard Worker continue;
81*d9f75844SAndroid Build Coastguard Worker }
82*d9f75844SAndroid Build Coastguard Worker double score =
83*d9f75844SAndroid Build Coastguard Worker I420SSE(*captured.thumb->GetI420(), *decoded->thumb->GetI420());
84*d9f75844SAndroid Build Coastguard Worker if (score < captured.best_score) {
85*d9f75844SAndroid Build Coastguard Worker captured.best_score = score;
86*d9f75844SAndroid Build Coastguard Worker captured.best_decode = decoded;
87*d9f75844SAndroid Build Coastguard Worker captured.matched = false;
88*d9f75844SAndroid Build Coastguard Worker } else {
89*d9f75844SAndroid Build Coastguard Worker captured.matched = true;
90*d9f75844SAndroid Build Coastguard Worker }
91*d9f75844SAndroid Build Coastguard Worker }
92*d9f75844SAndroid Build Coastguard Worker while (!layer.captured_frames.empty() &&
93*d9f75844SAndroid Build Coastguard Worker layer.captured_frames.front().matched) {
94*d9f75844SAndroid Build Coastguard Worker HandleMatch(std::move(layer.captured_frames.front()), layer_id);
95*d9f75844SAndroid Build Coastguard Worker layer.captured_frames.pop_front();
96*d9f75844SAndroid Build Coastguard Worker }
97*d9f75844SAndroid Build Coastguard Worker });
98*d9f75844SAndroid Build Coastguard Worker }
99*d9f75844SAndroid Build Coastguard Worker
Active() const100*d9f75844SAndroid Build Coastguard Worker bool VideoFrameMatcher::Active() const {
101*d9f75844SAndroid Build Coastguard Worker return !frame_pair_handlers_.empty();
102*d9f75844SAndroid Build Coastguard Worker }
103*d9f75844SAndroid Build Coastguard Worker
HandleMatch(VideoFrameMatcher::CapturedFrame captured,int layer_id)104*d9f75844SAndroid Build Coastguard Worker void VideoFrameMatcher::HandleMatch(VideoFrameMatcher::CapturedFrame captured,
105*d9f75844SAndroid Build Coastguard Worker int layer_id) {
106*d9f75844SAndroid Build Coastguard Worker VideoFramePair frame_pair;
107*d9f75844SAndroid Build Coastguard Worker frame_pair.layer_id = layer_id;
108*d9f75844SAndroid Build Coastguard Worker frame_pair.captured = captured.frame;
109*d9f75844SAndroid Build Coastguard Worker frame_pair.capture_id = captured.id;
110*d9f75844SAndroid Build Coastguard Worker frame_pair.capture_time = captured.capture_time;
111*d9f75844SAndroid Build Coastguard Worker if (captured.best_decode) {
112*d9f75844SAndroid Build Coastguard Worker frame_pair.decode_id = captured.best_decode->id;
113*d9f75844SAndroid Build Coastguard Worker frame_pair.decoded = captured.best_decode->frame;
114*d9f75844SAndroid Build Coastguard Worker frame_pair.decoded_time = captured.best_decode->decoded_time;
115*d9f75844SAndroid Build Coastguard Worker // We can't render frames before they have been decoded.
116*d9f75844SAndroid Build Coastguard Worker frame_pair.render_time = std::max(captured.best_decode->render_time,
117*d9f75844SAndroid Build Coastguard Worker captured.best_decode->decoded_time);
118*d9f75844SAndroid Build Coastguard Worker frame_pair.repeated = captured.best_decode->repeat_count++;
119*d9f75844SAndroid Build Coastguard Worker }
120*d9f75844SAndroid Build Coastguard Worker for (auto& handler : frame_pair_handlers_)
121*d9f75844SAndroid Build Coastguard Worker handler(frame_pair);
122*d9f75844SAndroid Build Coastguard Worker }
123*d9f75844SAndroid Build Coastguard Worker
Finalize()124*d9f75844SAndroid Build Coastguard Worker void VideoFrameMatcher::Finalize() {
125*d9f75844SAndroid Build Coastguard Worker for (auto& layer : layers_) {
126*d9f75844SAndroid Build Coastguard Worker while (!layer.second.captured_frames.empty()) {
127*d9f75844SAndroid Build Coastguard Worker HandleMatch(std::move(layer.second.captured_frames.front()), layer.first);
128*d9f75844SAndroid Build Coastguard Worker layer.second.captured_frames.pop_front();
129*d9f75844SAndroid Build Coastguard Worker }
130*d9f75844SAndroid Build Coastguard Worker }
131*d9f75844SAndroid Build Coastguard Worker }
132*d9f75844SAndroid Build Coastguard Worker
CapturedFrameTap(Clock * clock,VideoFrameMatcher * matcher)133*d9f75844SAndroid Build Coastguard Worker CapturedFrameTap::CapturedFrameTap(Clock* clock, VideoFrameMatcher* matcher)
134*d9f75844SAndroid Build Coastguard Worker : clock_(clock), matcher_(matcher) {}
135*d9f75844SAndroid Build Coastguard Worker
OnFrame(const VideoFrame & frame)136*d9f75844SAndroid Build Coastguard Worker void CapturedFrameTap::OnFrame(const VideoFrame& frame) {
137*d9f75844SAndroid Build Coastguard Worker matcher_->OnCapturedFrame(frame, clock_->CurrentTime());
138*d9f75844SAndroid Build Coastguard Worker }
OnDiscardedFrame()139*d9f75844SAndroid Build Coastguard Worker void CapturedFrameTap::OnDiscardedFrame() {
140*d9f75844SAndroid Build Coastguard Worker discarded_count_++;
141*d9f75844SAndroid Build Coastguard Worker }
142*d9f75844SAndroid Build Coastguard Worker
ForwardingCapturedFrameTap(Clock * clock,VideoFrameMatcher * matcher,rtc::VideoSourceInterface<VideoFrame> * source)143*d9f75844SAndroid Build Coastguard Worker ForwardingCapturedFrameTap::ForwardingCapturedFrameTap(
144*d9f75844SAndroid Build Coastguard Worker Clock* clock,
145*d9f75844SAndroid Build Coastguard Worker VideoFrameMatcher* matcher,
146*d9f75844SAndroid Build Coastguard Worker rtc::VideoSourceInterface<VideoFrame>* source)
147*d9f75844SAndroid Build Coastguard Worker : clock_(clock), matcher_(matcher), source_(source) {}
148*d9f75844SAndroid Build Coastguard Worker
OnFrame(const VideoFrame & frame)149*d9f75844SAndroid Build Coastguard Worker void ForwardingCapturedFrameTap::OnFrame(const VideoFrame& frame) {
150*d9f75844SAndroid Build Coastguard Worker RTC_CHECK(sink_);
151*d9f75844SAndroid Build Coastguard Worker matcher_->OnCapturedFrame(frame, clock_->CurrentTime());
152*d9f75844SAndroid Build Coastguard Worker sink_->OnFrame(frame);
153*d9f75844SAndroid Build Coastguard Worker }
OnDiscardedFrame()154*d9f75844SAndroid Build Coastguard Worker void ForwardingCapturedFrameTap::OnDiscardedFrame() {
155*d9f75844SAndroid Build Coastguard Worker RTC_CHECK(sink_);
156*d9f75844SAndroid Build Coastguard Worker discarded_count_++;
157*d9f75844SAndroid Build Coastguard Worker sink_->OnDiscardedFrame();
158*d9f75844SAndroid Build Coastguard Worker }
159*d9f75844SAndroid Build Coastguard Worker
AddOrUpdateSink(VideoSinkInterface<VideoFrame> * sink,const rtc::VideoSinkWants & wants)160*d9f75844SAndroid Build Coastguard Worker void ForwardingCapturedFrameTap::AddOrUpdateSink(
161*d9f75844SAndroid Build Coastguard Worker VideoSinkInterface<VideoFrame>* sink,
162*d9f75844SAndroid Build Coastguard Worker const rtc::VideoSinkWants& wants) {
163*d9f75844SAndroid Build Coastguard Worker if (!sink_)
164*d9f75844SAndroid Build Coastguard Worker sink_ = sink;
165*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_EQ(sink_, sink);
166*d9f75844SAndroid Build Coastguard Worker source_->AddOrUpdateSink(this, wants);
167*d9f75844SAndroid Build Coastguard Worker }
RemoveSink(VideoSinkInterface<VideoFrame> * sink)168*d9f75844SAndroid Build Coastguard Worker void ForwardingCapturedFrameTap::RemoveSink(
169*d9f75844SAndroid Build Coastguard Worker VideoSinkInterface<VideoFrame>* sink) {
170*d9f75844SAndroid Build Coastguard Worker source_->RemoveSink(this);
171*d9f75844SAndroid Build Coastguard Worker sink_ = nullptr;
172*d9f75844SAndroid Build Coastguard Worker }
173*d9f75844SAndroid Build Coastguard Worker
DecodedFrameTap(Clock * clock,VideoFrameMatcher * matcher,int layer_id)174*d9f75844SAndroid Build Coastguard Worker DecodedFrameTap::DecodedFrameTap(Clock* clock,
175*d9f75844SAndroid Build Coastguard Worker VideoFrameMatcher* matcher,
176*d9f75844SAndroid Build Coastguard Worker int layer_id)
177*d9f75844SAndroid Build Coastguard Worker : clock_(clock), matcher_(matcher), layer_id_(layer_id) {
178*d9f75844SAndroid Build Coastguard Worker matcher_->RegisterLayer(layer_id_);
179*d9f75844SAndroid Build Coastguard Worker }
180*d9f75844SAndroid Build Coastguard Worker
OnFrame(const VideoFrame & frame)181*d9f75844SAndroid Build Coastguard Worker void DecodedFrameTap::OnFrame(const VideoFrame& frame) {
182*d9f75844SAndroid Build Coastguard Worker matcher_->OnDecodedFrame(frame, layer_id_,
183*d9f75844SAndroid Build Coastguard Worker Timestamp::Millis(frame.render_time_ms()),
184*d9f75844SAndroid Build Coastguard Worker clock_->CurrentTime());
185*d9f75844SAndroid Build Coastguard Worker }
186*d9f75844SAndroid Build Coastguard Worker
187*d9f75844SAndroid Build Coastguard Worker } // namespace test
188*d9f75844SAndroid Build Coastguard Worker } // namespace webrtc
189