/*
 *  Copyright (c) 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.
 */

#ifndef API_TEST_VIDEO_QUALITY_ANALYZER_INTERFACE_H_
#define API_TEST_VIDEO_QUALITY_ANALYZER_INTERFACE_H_

#include <memory>
#include <string>

#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "api/array_view.h"
#include "api/test/stats_observer_interface.h"
#include "api/video/encoded_image.h"
#include "api/video/video_frame.h"
#include "api/video_codecs/video_encoder.h"
#include "rtc_base/checks.h"

namespace webrtc {

// API is in development and can be changed without notice.

// Base interface for video quality analyzer for peer connection level end-2-end
// tests. Interface has only one abstract method, which have to return frame id.
// Other methods have empty implementation by default, so user can override only
// required parts.
//
// VideoQualityAnalyzerInterface will be injected into WebRTC pipeline on both
// sides of the call. Here is video data flow in WebRTC pipeline
//
// Alice:
//  ___________       ________       _________
// |           |     |        |     |         |
// |   Frame   |-(A)→| WebRTC |-(B)→| Video   |-(C)┐
// | Generator |     | Stack  |     | Decoder |    |
//  ¯¯¯¯¯¯¯¯¯¯¯       ¯¯¯¯¯¯¯¯       ¯¯¯¯¯¯¯¯¯     |
//                                               __↓________
//                                              | Transport |
//                                              |     &     |
//                                              |  Network  |
//                                               ¯¯|¯¯¯¯¯¯¯¯
// Bob:                                            |
//  _______       ________       _________         |
// |       |     |        |     |         |        |
// | Video |←(F)-| WebRTC |←(E)-| Video   |←(D)----┘
// | Sink  |     | Stack  |     | Decoder |
//  ¯¯¯¯¯¯¯       ¯¯¯¯¯¯¯¯       ¯¯¯¯¯¯¯¯¯
// The analyzer will be injected in all points from A to F.
class VideoQualityAnalyzerInterface
    : public webrtc_pc_e2e::StatsObserverInterface {
 public:
  // Contains extra statistic provided by video encoder.
  struct EncoderStats {
    std::string encoder_name = "unknown";
    // TODO(hbos) https://crbug.com/webrtc/9547,
    // https://crbug.com/webrtc/11443: improve stats API to make available
    // there.
    uint32_t target_encode_bitrate = 0;
    // Encoder quantizer value.
    int qp = -1;
  };
  // Contains extra statistic provided by video decoder.
  struct DecoderStats {
    std::string decoder_name = "unknown";
    // Decode time provided by decoder itself. If decoder doesn’t produce such
    // information can be omitted.
    absl::optional<int32_t> decode_time_ms = absl::nullopt;
  };

  ~VideoQualityAnalyzerInterface() override = default;

  // Will be called by framework before test.
  // `test_case_name` is name of test case, that should be used to report all
  // video metrics.
  // `threads_count` is number of threads that analyzer can use for heavy
  // calculations. Analyzer can perform simple calculations on the calling
  // thread in each method, but should remember, that it is the same thread,
  // that is used in video pipeline.
  virtual void Start(std::string test_case_name,
                     rtc::ArrayView<const std::string> peer_names,
                     int max_threads_count) {}

  // Will be called when frame was generated from the input stream.
  // `peer_name` is name of the peer on which side frame was captured.
  // Returns frame id, that will be set by framework to the frame.
  virtual uint16_t OnFrameCaptured(absl::string_view peer_name,
                                   const std::string& stream_label,
                                   const VideoFrame& frame) = 0;
  // Will be called before calling the encoder.
  // `peer_name` is name of the peer on which side frame came to encoder.
  virtual void OnFramePreEncode(absl::string_view peer_name,
                                const VideoFrame& frame) {}
  // Will be called for each EncodedImage received from encoder. Single
  // VideoFrame can produce multiple EncodedImages. Each encoded image will
  // have id from VideoFrame.
  // `peer_name` is name of the peer on which side frame was encoded.
  virtual void OnFrameEncoded(absl::string_view peer_name,
                              uint16_t frame_id,
                              const EncodedImage& encoded_image,
                              const EncoderStats& stats,
                              bool discarded) {}
  // Will be called for each frame dropped by encoder.
  // `peer_name` is name of the peer on which side frame drop was detected.
  virtual void OnFrameDropped(absl::string_view peer_name,
                              EncodedImageCallback::DropReason reason) {}
  // Will be called before calling the decoder.
  // `peer_name` is name of the peer on which side frame was received.
  virtual void OnFramePreDecode(absl::string_view peer_name,
                                uint16_t frame_id,
                                const EncodedImage& encoded_image) {}
  // Will be called after decoding the frame.
  // `peer_name` is name of the peer on which side frame was decoded.
  virtual void OnFrameDecoded(absl::string_view peer_name,
                              const VideoFrame& frame,
                              const DecoderStats& stats) {}
  // Will be called when frame will be obtained from PeerConnection stack.
  // `peer_name` is name of the peer on which side frame was rendered.
  virtual void OnFrameRendered(absl::string_view peer_name,
                               const VideoFrame& frame) {}
  // Will be called if encoder return not WEBRTC_VIDEO_CODEC_OK.
  // All available codes are listed in
  // modules/video_coding/include/video_error_codes.h
  // `peer_name` is name of the peer on which side error acquired.
  virtual void OnEncoderError(absl::string_view peer_name,
                              const VideoFrame& frame,
                              int32_t error_code) {}
  // Will be called if decoder return not WEBRTC_VIDEO_CODEC_OK.
  // All available codes are listed in
  // modules/video_coding/include/video_error_codes.h
  // `peer_name` is name of the peer on which side error acquired.
  virtual void OnDecoderError(absl::string_view peer_name,
                              uint16_t frame_id,
                              int32_t error_code,
                              const DecoderStats& stats) {}
  // Will be called every time new stats reports are available for the
  // Peer Connection identified by `pc_label`.
  void OnStatsReports(
      absl::string_view pc_label,
      const rtc::scoped_refptr<const RTCStatsReport>& report) override {}

  // Will be called before test adds new participant in the middle of a call.
  virtual void RegisterParticipantInCall(absl::string_view peer_name) {}
  // Will be called after test removed existing participant in the middle of the
  // call.
  virtual void UnregisterParticipantInCall(absl::string_view peer_name) {}

  // Informs analyzer that peer `receiver_peer_name` should not receive any
  // stream from sender `sender_peer_name`.
  // This method is a no-op if the sender or the receiver does not exist.
  virtual void OnPauseAllStreamsFrom(absl::string_view sender_peer_name,
                                     absl::string_view receiver_peer_name) {}

  // Informs analyzer that peer `receiver_peer_name` is expected to receive all
  // streams from `sender_peer_name`.
  // This method is a no-op if the sender or the receiver does not exist.
  virtual void OnResumeAllStreamsFrom(absl::string_view sender_peer_name,
                                      absl::string_view receiver_peer_name) {}

  // Tells analyzer that analysis complete and it should calculate final
  // statistics.
  virtual void Stop() {}

  // Returns the last stream where this frame was captured. It means that if
  // frame ids space wraps around, then stream label for frame id may change.
  // It will crash, if the specified `frame_id` wasn't captured.
  virtual std::string GetStreamLabel(uint16_t frame_id) = 0;

  // Returns the sender peer name of the last stream where this frame was
  // captured. The sender for this frame id may change when the frame ids wrap
  // around. Also it will crash, if the specified `frame_id` wasn't captured.
  virtual std::string GetSenderPeerName(uint16_t frame_id) const {
    RTC_CHECK(false) << "Not implemented.";
  }
};

}  // namespace webrtc

#endif  // API_TEST_VIDEO_QUALITY_ANALYZER_INTERFACE_H_
