text: /*
   *  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;
      frame_pair.render_time = captured.best_decode->render_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::ms(frame.render_time_ms()),
                             clock_->CurrentTime());
  }
  
  }  // namespace test
  }  // namespace webrtc
  