Sebastian Jansson | 9a4f38e | 2018-12-19 13:14:41 +0100 | [diff] [blame] | 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 | */ |
Sebastian Jansson | 7150d8c | 2019-04-09 14:18:09 +0200 | [diff] [blame] | 10 | #include "test/scenario/video_frame_matcher.h" |
Sebastian Jansson | 9a4f38e | 2018-12-19 13:14:41 +0100 | [diff] [blame] | 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 { |
Sebastian Jansson | cf2df2f | 2019-04-02 11:51:28 +0200 | [diff] [blame] | 20 | namespace { |
| 21 | constexpr int kThumbWidth = 96; |
| 22 | constexpr int kThumbHeight = 96; |
| 23 | } // namespace |
Sebastian Jansson | 9a4f38e | 2018-12-19 13:14:41 +0100 | [diff] [blame] | 24 | |
Sebastian Jansson | cf2df2f | 2019-04-02 11:51:28 +0200 | [diff] [blame] | 25 | VideoFrameMatcher::VideoFrameMatcher( |
| 26 | std::vector<std::function<void(const VideoFramePair&)> > |
| 27 | frame_pair_handlers) |
Sebastian Jansson | e9cac4f | 2019-06-24 17:10:55 +0200 | [diff] [blame] | 28 | : frame_pair_handlers_(std::move(frame_pair_handlers)), |
| 29 | task_queue_("VideoAnalyzer") {} |
Sebastian Jansson | 9a4f38e | 2018-12-19 13:14:41 +0100 | [diff] [blame] | 30 | |
Sebastian Jansson | cf2df2f | 2019-04-02 11:51:28 +0200 | [diff] [blame] | 31 | VideoFrameMatcher::~VideoFrameMatcher() { |
Sebastian Jansson | 7237c15 | 2019-04-08 16:47:49 +0200 | [diff] [blame] | 32 | task_queue_.SendTask([this] { Finalize(); }); |
Sebastian Jansson | 9a4f38e | 2018-12-19 13:14:41 +0100 | [diff] [blame] | 33 | } |
| 34 | |
Sebastian Jansson | cf2df2f | 2019-04-02 11:51:28 +0200 | [diff] [blame] | 35 | void VideoFrameMatcher::RegisterLayer(int layer_id) { |
| 36 | task_queue_.PostTask([this, layer_id] { layers_[layer_id] = VideoLayer(); }); |
| 37 | } |
| 38 | |
| 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; |
Sebastian Jansson | d0aec91 | 2019-04-17 13:22:27 +0200 | [diff] [blame] | 50 | if (layer.second.last_decode && |
| 51 | layer.second.last_decode->frame->width() <= captured.frame->width()) { |
Sebastian Jansson | cf2df2f | 2019-04-02 11:51:28 +0200 | [diff] [blame] | 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 | } |
Sebastian Jansson | 9a4f38e | 2018-12-19 13:14:41 +0100 | [diff] [blame] | 58 | }); |
| 59 | } |
| 60 | |
Sebastian Jansson | cf2df2f | 2019-04-02 11:51:28 +0200 | [diff] [blame] | 61 | void VideoFrameMatcher::OnDecodedFrame(const VideoFrame& frame, |
Sebastian Jansson | e9cac4f | 2019-06-24 17:10:55 +0200 | [diff] [blame] | 62 | int layer_id, |
Sebastian Jansson | cf2df2f | 2019-04-02 11:51:28 +0200 | [diff] [blame] | 63 | Timestamp render_time, |
Sebastian Jansson | e9cac4f | 2019-06-24 17:10:55 +0200 | [diff] [blame] | 64 | Timestamp at_time) { |
Sebastian Jansson | cf2df2f | 2019-04-02 11:51:28 +0200 | [diff] [blame] | 65 | rtc::scoped_refptr<DecodedFrame> decoded(new DecodedFrame{}); |
Sebastian Jansson | e9cac4f | 2019-06-24 17:10:55 +0200 | [diff] [blame] | 66 | decoded->decoded_time = at_time; |
Sebastian Jansson | cf2df2f | 2019-04-02 11:51:28 +0200 | [diff] [blame] | 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); |
Sebastian Jansson | cf2df2f | 2019-04-02 11:51:28 +0200 | [diff] [blame] | 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) { |
Sebastian Jansson | d0aec91 | 2019-04-17 13:22:27 +0200 | [diff] [blame] | 77 | // We can't match with a smaller capture. |
| 78 | if (captured.frame->width() < decoded->frame->width()) { |
| 79 | captured.matched = true; |
| 80 | continue; |
| 81 | } |
Sebastian Jansson | cf2df2f | 2019-04-02 11:51:28 +0200 | [diff] [blame] | 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) { |
Sebastian Jansson | 7150d8c | 2019-04-09 14:18:09 +0200 | [diff] [blame] | 94 | HandleMatch(std::move(layer.captured_frames.front()), layer_id); |
Sebastian Jansson | cf2df2f | 2019-04-02 11:51:28 +0200 | [diff] [blame] | 95 | layer.captured_frames.pop_front(); |
| 96 | } |
Sebastian Jansson | 9a4f38e | 2018-12-19 13:14:41 +0100 | [diff] [blame] | 97 | }); |
| 98 | } |
| 99 | |
Sebastian Jansson | cf2df2f | 2019-04-02 11:51:28 +0200 | [diff] [blame] | 100 | bool VideoFrameMatcher::Active() const { |
| 101 | return !frame_pair_handlers_.empty(); |
Sebastian Jansson | 9a4f38e | 2018-12-19 13:14:41 +0100 | [diff] [blame] | 102 | } |
| 103 | |
Sebastian Jansson | 7150d8c | 2019-04-09 14:18:09 +0200 | [diff] [blame] | 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; |
Sebastian Jansson | dab21c6 | 2019-05-13 11:48:40 +0200 | [diff] [blame] | 110 | frame_pair.capture_time = captured.capture_time; |
Sebastian Jansson | 7150d8c | 2019-04-09 14:18:09 +0200 | [diff] [blame] | 111 | if (captured.best_decode) { |
| 112 | frame_pair.decode_id = captured.best_decode->id; |
Sebastian Jansson | 7150d8c | 2019-04-09 14:18:09 +0200 | [diff] [blame] | 113 | frame_pair.decoded = captured.best_decode->frame; |
Sebastian Jansson | e9cac4f | 2019-06-24 17:10:55 +0200 | [diff] [blame] | 114 | frame_pair.decoded_time = captured.best_decode->decoded_time; |
Sebastian Jansson | 7150d8c | 2019-04-09 14:18:09 +0200 | [diff] [blame] | 115 | frame_pair.render_time = captured.best_decode->render_time; |
| 116 | frame_pair.repeated = captured.best_decode->repeat_count++; |
| 117 | } |
| 118 | for (auto& handler : frame_pair_handlers_) |
| 119 | handler(frame_pair); |
| 120 | } |
| 121 | |
Sebastian Jansson | cf2df2f | 2019-04-02 11:51:28 +0200 | [diff] [blame] | 122 | void VideoFrameMatcher::Finalize() { |
| 123 | for (auto& layer : layers_) { |
| 124 | while (!layer.second.captured_frames.empty()) { |
Sebastian Jansson | 7150d8c | 2019-04-09 14:18:09 +0200 | [diff] [blame] | 125 | HandleMatch(std::move(layer.second.captured_frames.front()), layer.first); |
Sebastian Jansson | cf2df2f | 2019-04-02 11:51:28 +0200 | [diff] [blame] | 126 | layer.second.captured_frames.pop_front(); |
| 127 | } |
Sebastian Jansson | 9a4f38e | 2018-12-19 13:14:41 +0100 | [diff] [blame] | 128 | } |
| 129 | } |
| 130 | |
Sebastian Jansson | e05ae5b | 2019-07-30 18:12:54 +0200 | [diff] [blame] | 131 | CapturedFrameTap::CapturedFrameTap(Clock* clock, VideoFrameMatcher* matcher) |
| 132 | : clock_(clock), matcher_(matcher) {} |
| 133 | |
| 134 | void CapturedFrameTap::OnFrame(const VideoFrame& frame) { |
| 135 | matcher_->OnCapturedFrame(frame, clock_->CurrentTime()); |
| 136 | } |
| 137 | void CapturedFrameTap::OnDiscardedFrame() { |
| 138 | discarded_count_++; |
| 139 | } |
| 140 | |
Sebastian Jansson | 9a4f38e | 2018-12-19 13:14:41 +0100 | [diff] [blame] | 141 | ForwardingCapturedFrameTap::ForwardingCapturedFrameTap( |
Sebastian Jansson | aa01f27 | 2019-01-30 11:28:59 +0100 | [diff] [blame] | 142 | Clock* clock, |
Sebastian Jansson | cf2df2f | 2019-04-02 11:51:28 +0200 | [diff] [blame] | 143 | VideoFrameMatcher* matcher, |
Sebastian Jansson | 9a4f38e | 2018-12-19 13:14:41 +0100 | [diff] [blame] | 144 | rtc::VideoSourceInterface<VideoFrame>* source) |
Sebastian Jansson | cf2df2f | 2019-04-02 11:51:28 +0200 | [diff] [blame] | 145 | : clock_(clock), matcher_(matcher), source_(source) {} |
Sebastian Jansson | 9a4f38e | 2018-12-19 13:14:41 +0100 | [diff] [blame] | 146 | |
Sebastian Jansson | 9a4f38e | 2018-12-19 13:14:41 +0100 | [diff] [blame] | 147 | void ForwardingCapturedFrameTap::OnFrame(const VideoFrame& frame) { |
| 148 | RTC_CHECK(sink_); |
Sebastian Jansson | b64ad0e | 2019-06-19 09:39:34 +0200 | [diff] [blame] | 149 | matcher_->OnCapturedFrame(frame, clock_->CurrentTime()); |
Sebastian Jansson | cf2df2f | 2019-04-02 11:51:28 +0200 | [diff] [blame] | 150 | sink_->OnFrame(frame); |
Sebastian Jansson | 9a4f38e | 2018-12-19 13:14:41 +0100 | [diff] [blame] | 151 | } |
| 152 | void ForwardingCapturedFrameTap::OnDiscardedFrame() { |
| 153 | RTC_CHECK(sink_); |
| 154 | discarded_count_++; |
| 155 | sink_->OnDiscardedFrame(); |
| 156 | } |
| 157 | |
| 158 | void ForwardingCapturedFrameTap::AddOrUpdateSink( |
| 159 | VideoSinkInterface<VideoFrame>* sink, |
| 160 | const rtc::VideoSinkWants& wants) { |
Sebastian Jansson | 7fa4277 | 2019-08-28 20:49:55 +0200 | [diff] [blame^] | 161 | if (!sink_) |
| 162 | sink_ = sink; |
| 163 | RTC_DCHECK_EQ(sink_, sink); |
Sebastian Jansson | 9a4f38e | 2018-12-19 13:14:41 +0100 | [diff] [blame] | 164 | source_->AddOrUpdateSink(this, wants); |
| 165 | } |
| 166 | void ForwardingCapturedFrameTap::RemoveSink( |
| 167 | VideoSinkInterface<VideoFrame>* sink) { |
| 168 | source_->RemoveSink(this); |
| 169 | sink_ = nullptr; |
| 170 | } |
| 171 | |
Sebastian Jansson | e9cac4f | 2019-06-24 17:10:55 +0200 | [diff] [blame] | 172 | DecodedFrameTap::DecodedFrameTap(Clock* clock, |
| 173 | VideoFrameMatcher* matcher, |
| 174 | int layer_id) |
| 175 | : clock_(clock), matcher_(matcher), layer_id_(layer_id) { |
Sebastian Jansson | cf2df2f | 2019-04-02 11:51:28 +0200 | [diff] [blame] | 176 | matcher_->RegisterLayer(layer_id_); |
| 177 | } |
Sebastian Jansson | 9a4f38e | 2018-12-19 13:14:41 +0100 | [diff] [blame] | 178 | |
| 179 | void DecodedFrameTap::OnFrame(const VideoFrame& frame) { |
Sebastian Jansson | e9cac4f | 2019-06-24 17:10:55 +0200 | [diff] [blame] | 180 | matcher_->OnDecodedFrame(frame, layer_id_, |
| 181 | Timestamp::ms(frame.render_time_ms()), |
| 182 | clock_->CurrentTime()); |
Sebastian Jansson | cf2df2f | 2019-04-02 11:51:28 +0200 | [diff] [blame] | 183 | } |
| 184 | |
Sebastian Jansson | 9a4f38e | 2018-12-19 13:14:41 +0100 | [diff] [blame] | 185 | } // namespace test |
| 186 | } // namespace webrtc |