xref: /aosp_15_r20/external/webrtc/test/scenario/video_frame_matcher.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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