diff options
Diffstat (limited to 'third_party/libwebrtc/test/scenario/video_frame_matcher.cc')
-rw-r--r-- | third_party/libwebrtc/test/scenario/video_frame_matcher.cc | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/third_party/libwebrtc/test/scenario/video_frame_matcher.cc b/third_party/libwebrtc/test/scenario/video_frame_matcher.cc new file mode 100644 index 0000000000..dc8cd59756 --- /dev/null +++ b/third_party/libwebrtc/test/scenario/video_frame_matcher.cc @@ -0,0 +1,188 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "test/scenario/video_frame_matcher.h" + +#include <utility> + +#include "common_video/libyuv/include/webrtc_libyuv.h" +#include "rtc_base/checks.h" +#include "rtc_base/event.h" + +namespace webrtc { +namespace test { +namespace { +constexpr int kThumbWidth = 96; +constexpr int kThumbHeight = 96; +} // namespace + +VideoFrameMatcher::VideoFrameMatcher( + std::vector<std::function<void(const VideoFramePair&)> > + frame_pair_handlers) + : frame_pair_handlers_(std::move(frame_pair_handlers)), + task_queue_("VideoAnalyzer") {} + +VideoFrameMatcher::~VideoFrameMatcher() { + task_queue_.SendTask([this] { Finalize(); }); +} + +void VideoFrameMatcher::RegisterLayer(int layer_id) { + task_queue_.PostTask([this, layer_id] { layers_[layer_id] = VideoLayer(); }); +} + +void VideoFrameMatcher::OnCapturedFrame(const VideoFrame& frame, + Timestamp at_time) { + CapturedFrame captured; + captured.id = next_capture_id_++; + captured.capture_time = at_time; + captured.frame = frame.video_frame_buffer(); + captured.thumb = ScaleVideoFrameBuffer(*frame.video_frame_buffer()->ToI420(), + kThumbWidth, kThumbHeight), + task_queue_.PostTask([this, captured]() { + for (auto& layer : layers_) { + CapturedFrame copy = captured; + if (layer.second.last_decode && + layer.second.last_decode->frame->width() <= captured.frame->width()) { + copy.best_score = I420SSE(*captured.thumb->GetI420(), + *layer.second.last_decode->thumb->GetI420()); + copy.best_decode = layer.second.last_decode; + } + layer.second.captured_frames.push_back(std::move(copy)); + } + }); +} + +void VideoFrameMatcher::OnDecodedFrame(const VideoFrame& frame, + int layer_id, + Timestamp render_time, + Timestamp at_time) { + rtc::scoped_refptr<DecodedFrame> decoded(new DecodedFrame{}); + decoded->decoded_time = at_time; + decoded->render_time = render_time; + decoded->frame = frame.video_frame_buffer(); + decoded->thumb = ScaleVideoFrameBuffer(*frame.video_frame_buffer()->ToI420(), + kThumbWidth, kThumbHeight); + + task_queue_.PostTask([this, decoded, layer_id] { + auto& layer = layers_[layer_id]; + decoded->id = layer.next_decoded_id++; + layer.last_decode = decoded; + for (auto& captured : layer.captured_frames) { + // We can't match with a smaller capture. + if (captured.frame->width() < decoded->frame->width()) { + captured.matched = true; + continue; + } + double score = + I420SSE(*captured.thumb->GetI420(), *decoded->thumb->GetI420()); + if (score < captured.best_score) { + captured.best_score = score; + captured.best_decode = decoded; + captured.matched = false; + } else { + captured.matched = true; + } + } + while (!layer.captured_frames.empty() && + layer.captured_frames.front().matched) { + HandleMatch(std::move(layer.captured_frames.front()), layer_id); + layer.captured_frames.pop_front(); + } + }); +} + +bool VideoFrameMatcher::Active() const { + return !frame_pair_handlers_.empty(); +} + +void VideoFrameMatcher::HandleMatch(VideoFrameMatcher::CapturedFrame captured, + int layer_id) { + VideoFramePair frame_pair; + frame_pair.layer_id = layer_id; + frame_pair.captured = captured.frame; + frame_pair.capture_id = captured.id; + frame_pair.capture_time = captured.capture_time; + if (captured.best_decode) { + frame_pair.decode_id = captured.best_decode->id; + frame_pair.decoded = captured.best_decode->frame; + frame_pair.decoded_time = captured.best_decode->decoded_time; + // We can't render frames before they have been decoded. + frame_pair.render_time = std::max(captured.best_decode->render_time, + captured.best_decode->decoded_time); + frame_pair.repeated = captured.best_decode->repeat_count++; + } + for (auto& handler : frame_pair_handlers_) + handler(frame_pair); +} + +void VideoFrameMatcher::Finalize() { + for (auto& layer : layers_) { + while (!layer.second.captured_frames.empty()) { + HandleMatch(std::move(layer.second.captured_frames.front()), layer.first); + layer.second.captured_frames.pop_front(); + } + } +} + +CapturedFrameTap::CapturedFrameTap(Clock* clock, VideoFrameMatcher* matcher) + : clock_(clock), matcher_(matcher) {} + +void CapturedFrameTap::OnFrame(const VideoFrame& frame) { + matcher_->OnCapturedFrame(frame, clock_->CurrentTime()); +} +void CapturedFrameTap::OnDiscardedFrame() { + discarded_count_++; +} + +ForwardingCapturedFrameTap::ForwardingCapturedFrameTap( + Clock* clock, + VideoFrameMatcher* matcher, + rtc::VideoSourceInterface<VideoFrame>* source) + : clock_(clock), matcher_(matcher), source_(source) {} + +void ForwardingCapturedFrameTap::OnFrame(const VideoFrame& frame) { + RTC_CHECK(sink_); + matcher_->OnCapturedFrame(frame, clock_->CurrentTime()); + sink_->OnFrame(frame); +} +void ForwardingCapturedFrameTap::OnDiscardedFrame() { + RTC_CHECK(sink_); + discarded_count_++; + sink_->OnDiscardedFrame(); +} + +void ForwardingCapturedFrameTap::AddOrUpdateSink( + VideoSinkInterface<VideoFrame>* sink, + const rtc::VideoSinkWants& wants) { + if (!sink_) + sink_ = sink; + RTC_DCHECK_EQ(sink_, sink); + source_->AddOrUpdateSink(this, wants); +} +void ForwardingCapturedFrameTap::RemoveSink( + VideoSinkInterface<VideoFrame>* sink) { + source_->RemoveSink(this); + sink_ = nullptr; +} + +DecodedFrameTap::DecodedFrameTap(Clock* clock, + VideoFrameMatcher* matcher, + int layer_id) + : clock_(clock), matcher_(matcher), layer_id_(layer_id) { + matcher_->RegisterLayer(layer_id_); +} + +void DecodedFrameTap::OnFrame(const VideoFrame& frame) { + matcher_->OnDecodedFrame(frame, layer_id_, + Timestamp::Millis(frame.render_time_ms()), + clock_->CurrentTime()); +} + +} // namespace test +} // namespace webrtc |