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