/*
 *  Copyright (c) 2022 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 MODULES_VIDEO_CODING_TIMING_FRAME_DELAY_VARIATION_KALMAN_FILTER_H_
#define MODULES_VIDEO_CODING_TIMING_FRAME_DELAY_VARIATION_KALMAN_FILTER_H_

#include "api/units/data_size.h"
#include "api/units/time_delta.h"

namespace webrtc {

// This class uses a linear Kalman filter (see
// https://en.wikipedia.org/wiki/Kalman_filter) to estimate the frame delay
// variation (i.e., the difference in transmission time between a frame and the
// prior frame) for a frame, given its size variation in bytes (i.e., the
// difference in size between a frame and the prior frame). The idea is that,
// given a fixed link bandwidth, a larger frame (in bytes) would take
// proportionally longer to arrive than a correspondingly smaller frame. Using
// the variations of frame delay and frame size, the underlying bandwidth and
// queuing delay variation of the network link can be estimated.
//
// The filter takes as input the frame delay variation, the difference between
// the actual inter-frame arrival time and the expected inter-frame arrival time
// (based on RTP timestamp), and frame size variation, the inter-frame size
// delta for a single frame. The frame delay variation is seen as the
// measurement and the frame size variation is used in the observation model.
// The hidden state of the filter is the link bandwidth and queuing delay
// buildup. The estimated state can be used to get the expected frame delay
// variation for a frame, given its frame size variation. This information can
// then be used to estimate the frame delay variation coming from network
// jitter.
//
// Mathematical details:
//  * The state (`x` in Wikipedia notation) is a 2x1 vector comprising the
//    reciprocal of link bandwidth [1 / bytes per ms] and the
//    link queuing delay buildup [ms].
//  * The state transition matrix (`F`) is the 2x2 identity matrix, meaning that
//    link bandwidth and link queuing delay buildup are modeled as independent.
//  * The measurement (`z`) is the (scalar) frame delay variation [ms].
//  * The observation matrix (`H`) is a 1x2 vector set as
//    `{frame_size_variation [bytes], 1.0}`.
//  * The state estimate covariance (`P`) is a symmetric 2x2 matrix.
//  * The process noise covariance (`Q`) is a constant 2x2 diagonal matrix
//    [(1 / bytes per ms)^2, ms^2].
//  * The observation noise covariance (`r`) is a scalar [ms^2] that is
//    determined externally to this class.
class FrameDelayVariationKalmanFilter {
 public:
  FrameDelayVariationKalmanFilter();
  ~FrameDelayVariationKalmanFilter() = default;

  // Predicts and updates the filter, given a new pair of frame delay variation
  // and frame size variation.
  //
  // Inputs:
  // `frame_delay_variation_ms`:
  //    Frame delay variation as calculated by the `InterFrameDelay` estimator.
  //
  // `frame_size_variation_bytes`:
  //    Frame size variation, i.e., the current frame size minus the previous
  //    frame size (in bytes). Note that this quantity may be negative.
  //
  // `max_frame_size_bytes`:
  //    Filtered largest frame size received since the last reset.
  //
  // `var_noise`:
  //    Variance of the estimated random jitter.
  //
  // TODO(bugs.webrtc.org/14381): For now use doubles as input parameters as
  // units defined in api/units have insufficient underlying precision for
  // jitter estimation.
  void PredictAndUpdate(double frame_delay_variation_ms,
                        double frame_size_variation_bytes,
                        double max_frame_size_bytes,
                        double var_noise);

  // Given a frame size variation, returns the estimated frame delay variation
  // explained by the link bandwidth alone.
  double GetFrameDelayVariationEstimateSizeBased(
      double frame_size_variation_bytes) const;

  // Given a frame size variation, returns the estimated frame delay variation
  // explained by both link bandwidth and link queuing delay buildup.
  double GetFrameDelayVariationEstimateTotal(
      double frame_size_variation_bytes) const;

 private:
  // State estimate (bandwidth [1 / bytes per ms], queue buildup [ms]).
  double estimate_[2];
  double estimate_cov_[2][2];  // Estimate covariance.

  // Process noise covariance. This is a diagonal matrix, so we only store the
  // diagonal entries.
  double process_noise_cov_diag_[2];
};

}  // namespace webrtc

#endif  // MODULES_VIDEO_CODING_TIMING_FRAME_DELAY_VARIATION_KALMAN_FILTER_H_
