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