From 754654a207f64c44bd84754fb88bc5fa52c11d05 Mon Sep 17 00:00:00 2001 From: Victor Gaydov Date: Sat, 3 Aug 2024 00:00:40 +0400 Subject: [PATCH] chore: Rework FreqEstimator and LinkMeter configuration FreqEstimatorConfig and LinkMeterConfig are now part of pipeline config. LatencyConfig now does not contains settings for LinkMeter (window length). Instead, FreqEstimatorConfig and LinkMeterConfig both have deduce_defaults() which set values based on latency profile. This allows to pass custom settings to FreqEstimator and LinkMeter in pipeline tests for adaptive latency. --- .../roc_audio/feedback_monitor.cpp | 3 +- .../roc_audio/feedback_monitor.h | 1 + .../roc_audio/freq_estimator.cpp | 85 +++++----- .../roc_audio/freq_estimator.h | 31 ++-- .../roc_audio/latency_config.cpp | 14 -- .../roc_audio/latency_config.h | 10 +- .../roc_audio/latency_monitor.cpp | 7 +- .../roc_audio/latency_monitor.h | 3 +- .../roc_audio/latency_tuner.cpp | 160 +++++++++--------- .../roc_audio/latency_tuner.h | 3 +- .../roc_audio/resampler_config.cpp | 11 +- src/internal_modules/roc_pipeline/config.cpp | 18 +- src/internal_modules/roc_pipeline/config.h | 21 ++- .../roc_pipeline/receiver_session.cpp | 6 +- .../roc_pipeline/sender_session.cpp | 2 +- src/internal_modules/roc_rtp/link_meter.cpp | 17 +- src/internal_modules/roc_rtp/link_meter.h | 17 +- src/tests/roc_audio/test_freq_estimator.cpp | 36 ++-- src/tests/roc_rtp/test_link_meter.cpp | 9 +- 19 files changed, 255 insertions(+), 199 deletions(-) diff --git a/src/internal_modules/roc_audio/feedback_monitor.cpp b/src/internal_modules/roc_audio/feedback_monitor.cpp index 268d50e79..95f80a3dd 100644 --- a/src/internal_modules/roc_audio/feedback_monitor.cpp +++ b/src/internal_modules/roc_audio/feedback_monitor.cpp @@ -20,9 +20,10 @@ FeedbackMonitor::FeedbackMonitor(IFrameWriter& writer, ResamplerWriter* resampler, const FeedbackConfig& feedback_config, const LatencyConfig& latency_config, + const FreqEstimatorConfig& fe_config, const SampleSpec& sample_spec, dbgio::CsvDumper* dumper) - : tuner_(latency_config, sample_spec, dumper) + : tuner_(latency_config, fe_config, sample_spec, dumper) , use_packetizer_(false) , has_feedback_(false) , last_feedback_ts_(0) diff --git a/src/internal_modules/roc_audio/feedback_monitor.h b/src/internal_modules/roc_audio/feedback_monitor.h index 791e89de7..fb40a426f 100644 --- a/src/internal_modules/roc_audio/feedback_monitor.h +++ b/src/internal_modules/roc_audio/feedback_monitor.h @@ -69,6 +69,7 @@ class FeedbackMonitor : public IFrameWriter, public core::NonCopyable<> { ResamplerWriter* resampler, const FeedbackConfig& feedback_config, const LatencyConfig& latency_config, + const FreqEstimatorConfig& fe_config, const SampleSpec& sample_spec, dbgio::CsvDumper* dumper); diff --git a/src/internal_modules/roc_audio/freq_estimator.cpp b/src/internal_modules/roc_audio/freq_estimator.cpp index a1fcd6fc3..0b9ee04db 100644 --- a/src/internal_modules/roc_audio/freq_estimator.cpp +++ b/src/internal_modules/roc_audio/freq_estimator.cpp @@ -16,33 +16,6 @@ namespace audio { namespace { -// Make config from profile. -FreqEstimatorConfig make_config(FreqEstimatorProfile profile) { - FreqEstimatorConfig config; - - switch (profile) { - case FreqEstimatorProfile_Responsive: - config.P = 1e-6; - config.I = 1e-10; - config.decimation_factor1 = fe_decim_factor_max; - config.decimation_factor2 = 0; - config.stable_criteria = 0.1; - break; - - case FreqEstimatorProfile_Gradual: - config.P = 1e-6; - config.I = 5e-9; - config.decimation_factor1 = fe_decim_factor_max; - config.decimation_factor2 = fe_decim_factor_max; - config.stable_criteria = 0.05; - break; - } - config.stability_duration_criteria = 15 * core::Second; - config.control_action_saturation_cap = 1e-2; - - return config; -} - // Calculate dot product of arrays IR of filter (coeff) and input array (samples). // // - coeff: Filter impulse response. @@ -66,10 +39,52 @@ double dot_prod(const double* coeff, } // namespace -FreqEstimator::FreqEstimator(FreqEstimatorProfile profile, +bool FreqEstimatorConfig::deduce_defaults(LatencyTunerProfile latency_profile) { + switch (latency_profile) { + case LatencyTunerProfile_Gradual: + if (P == 0 && I == 0) { + P = 1e-6; + I = 5e-9; + } + if (decimation_factor1 == 0 && decimation_factor2 == 0) { + decimation_factor1 = fe_decim_factor_max; + decimation_factor2 = fe_decim_factor_max; + } + if (stable_criteria == 0) { + stable_criteria = 0.05; + } + break; + + case LatencyTunerProfile_Responsive: + if (P == 0 && I == 0) { + P = 1e-6; + I = 1e-10; + } + if (decimation_factor1 == 0 && decimation_factor2 == 0) { + decimation_factor1 = fe_decim_factor_max; + decimation_factor2 = 0; + } + if (stable_criteria == 0) { + stable_criteria = 0.1; + } + break; + + case LatencyTunerProfile_Intact: + break; + + default: + roc_log(LogError, "freq estimator: unexpected latency tuner profile %s", + latency_tuner_profile_to_str(latency_profile)); + return false; + } + + return true; +} + +FreqEstimator::FreqEstimator(const FreqEstimatorConfig& config, packet::stream_timestamp_t target_latency, dbgio::CsvDumper* dumper) - : config_(make_config(profile)) + : config_(config) , target_(target_latency) , dec1_ind_(0) , dec2_ind_(0) @@ -221,17 +236,5 @@ bool FreqEstimator::is_stable() const { return stable_; } -static const char* fe_profile_to_str(FreqEstimatorProfile profile) { - switch (profile) { - case FreqEstimatorProfile_Responsive: - return "responsive"; - - case FreqEstimatorProfile_Gradual: - return "gradual"; - } - - return ""; -} - } // namespace audio } // namespace roc diff --git a/src/internal_modules/roc_audio/freq_estimator.h b/src/internal_modules/roc_audio/freq_estimator.h index 9f186c4c1..8d42df9c9 100644 --- a/src/internal_modules/roc_audio/freq_estimator.h +++ b/src/internal_modules/roc_audio/freq_estimator.h @@ -13,7 +13,9 @@ #define ROC_AUDIO_FREQ_ESTIMATOR_H_ #include "roc_audio/freq_estimator_decim.h" +#include "roc_audio/latency_config.h" #include "roc_audio/sample.h" +#include "roc_core/attributes.h" #include "roc_core/noncopyable.h" #include "roc_dbgio/csv_dumper.h" #include "roc_packet/units.h" @@ -21,21 +23,13 @@ namespace roc { namespace audio { -//! FreqEstimator parameters preset. -enum FreqEstimatorProfile { - //! Fast and responsive tuning. - //! Good for lower network latency and jitter. - FreqEstimatorProfile_Responsive, - - //! Slow and smooth tuning. - //! Good for higher network latency and jitter. - FreqEstimatorProfile_Gradual -}; - //! FreqEstimator tunable parameters. struct FreqEstimatorConfig { - double P; //!< Proportional gain of PI-controller. - double I; //!< Integral gain of PI-controller. + //! Proportional gain of PI-controller. + double P; + + //! Integral gain of PI-controller. + double I; //! How much downsample input value (latency buffer size) on the first stage. //! Must be less or equal to fe_decim_factor_max and must be greater than zero. @@ -62,10 +56,13 @@ struct FreqEstimatorConfig { , I(0) , decimation_factor1(0) , decimation_factor2(0) - , stable_criteria(0.1) - , stability_duration_criteria(0) - , control_action_saturation_cap(0) { + , stable_criteria(0) + , stability_duration_criteria(15 * core::Second) + , control_action_saturation_cap(1e-2) { } + + //! Automatically fill missing settings. + ROC_ATTR_NODISCARD bool deduce_defaults(LatencyTunerProfile latency_profile); }; //! Evaluates sender's frequency to receiver's frequency ratio. @@ -82,7 +79,7 @@ class FreqEstimator : public core::NonCopyable<> { //! @b Parameters //! - @p profile defines configuration preset. //! - @p target_latency defines latency we want to archive. - FreqEstimator(FreqEstimatorProfile profile, + FreqEstimator(const FreqEstimatorConfig& config, packet::stream_timestamp_t target_latency, dbgio::CsvDumper* dumper); diff --git a/src/internal_modules/roc_audio/latency_config.cpp b/src/internal_modules/roc_audio/latency_config.cpp index 8a591acd2..2064615e6 100644 --- a/src/internal_modules/roc_audio/latency_config.cpp +++ b/src/internal_modules/roc_audio/latency_config.cpp @@ -104,16 +104,6 @@ core::nanoseconds_t deduce_stale_tolerance(const core::nanoseconds_t latency_tol return std::max(latency_tolerance / 4, 10 * core::Millisecond); } -size_t deduce_sliding_window_len(const LatencyTunerProfile tuner_profile) { - if (tuner_profile == audio::LatencyTunerProfile_Responsive) { - // Responsive profile requires faster reactions to changes - // of link characteristics. - return 10000; - } else { - return 30000; - } -} - bool validate_adaptive_latency(const core::nanoseconds_t target_latency, const core::nanoseconds_t latency_tolerance, const core::nanoseconds_t start_target_latency, @@ -267,10 +257,6 @@ bool LatencyConfig::deduce_defaults(core::nanoseconds_t default_latency, } } - if (sliding_window_length == 0) { - sliding_window_length = deduce_sliding_window_len(tuner_profile); - } - return true; } diff --git a/src/internal_modules/roc_audio/latency_config.h b/src/internal_modules/roc_audio/latency_config.h index 8248ceeeb..bc7146157 100644 --- a/src/internal_modules/roc_audio/latency_config.h +++ b/src/internal_modules/roc_audio/latency_config.h @@ -136,11 +136,6 @@ struct LatencyConfig { //! For example, 0.01 allows freq_coeff values in range [0.99; 1.01]. float scaling_tolerance; - //! Number of packets we use to calculate sliding statistics. - //! @remarks - //! We calculate jitter statistics based on this last delivered packets. - size_t sliding_window_length; - //! Latency tuner decides to adjust target latency if //! the current value >= estimated optimal latency * //! latency_decrease_relative_threshold_. @@ -182,7 +177,6 @@ struct LatencyConfig { , stale_tolerance(0) , scaling_interval(5 * core::Millisecond) , scaling_tolerance(0.005f) - , sliding_window_length(0) , latency_decrease_relative_threshold(1.7f) , starting_timeout(5 * core::Second) , cooldown_dec_timeout(5 * core::Second) @@ -207,11 +201,11 @@ struct LatencyMetrics { core::nanoseconds_t niq_stalling; //! Estimated end-to-end latency. - //! An estimate of the time from recording a frame on sender to playing it - //! on receiver. + //! An estimate of time from recording a frame on sender to playing it on receiver. core::nanoseconds_t e2e_latency; //! Estimated FEC block duration. + //! Total duration of packets within one FEC block. core::nanoseconds_t fec_block_duration; LatencyMetrics() diff --git a/src/internal_modules/roc_audio/latency_monitor.cpp b/src/internal_modules/roc_audio/latency_monitor.cpp index 6b28eb1b5..06926f2c4 100644 --- a/src/internal_modules/roc_audio/latency_monitor.cpp +++ b/src/internal_modules/roc_audio/latency_monitor.cpp @@ -21,18 +21,19 @@ LatencyMonitor::LatencyMonitor(IFrameReader& frame_reader, const packet::ILinkMeter& link_meter, const fec::BlockReader* fec_reader, ResamplerReader* resampler, - const LatencyConfig& config, + const LatencyConfig& latency_config, + const FreqEstimatorConfig& fe_config, const SampleSpec& packet_sample_spec, const SampleSpec& frame_sample_spec, dbgio::CsvDumper* dumper) - : tuner_(config, frame_sample_spec, dumper) + : tuner_(latency_config, fe_config, frame_sample_spec, dumper) , frame_reader_(frame_reader) , incoming_queue_(incoming_queue) , depacketizer_(depacketizer) , link_meter_(link_meter) , fec_reader_(fec_reader) , resampler_(resampler) - , enable_scaling_(config.tuner_profile != audio::LatencyTunerProfile_Intact) + , enable_scaling_(latency_config.tuner_profile != audio::LatencyTunerProfile_Intact) , capture_ts_(0) , packet_sample_spec_(packet_sample_spec) , frame_sample_spec_(frame_sample_spec) diff --git a/src/internal_modules/roc_audio/latency_monitor.h b/src/internal_modules/roc_audio/latency_monitor.h index da5164cc0..f60c0dfb4 100644 --- a/src/internal_modules/roc_audio/latency_monitor.h +++ b/src/internal_modules/roc_audio/latency_monitor.h @@ -67,7 +67,8 @@ class LatencyMonitor : public IFrameReader, public core::NonCopyable<> { const packet::ILinkMeter& link_meter, const fec::BlockReader* fec_reader, ResamplerReader* resampler, - const LatencyConfig& config, + const LatencyConfig& latency_config, + const FreqEstimatorConfig& fe_config, const SampleSpec& packet_sample_spec, const SampleSpec& frame_sample_spec, dbgio::CsvDumper* dumper); diff --git a/src/internal_modules/roc_audio/latency_tuner.cpp b/src/internal_modules/roc_audio/latency_tuner.cpp index a556b68ee..bb6e660e9 100644 --- a/src/internal_modules/roc_audio/latency_tuner.cpp +++ b/src/internal_modules/roc_audio/latency_tuner.cpp @@ -21,36 +21,39 @@ const core::nanoseconds_t LogInterval = 5 * core::Second; // Calculates latency decreasment step value such that // if current latency equals exactly upper threshold value, after the decreasment // it will get in the middle between threshold and estimated value. -float upper_coef_to_step_lat_update_(const float x) { +float upper_coef_to_step_lat_update(const float x) { return ((x + 1.f) / (x * 2.f)); } // Calculates latency increasment step value based on // latency_decrease_relative_threshold. -float lower_thrs_to_step_lat_update_(const float x) { +float lower_thrs_to_step_lat_update(const float x) { return (x + 1.f) / 2.f; } } // namespace -LatencyTuner::LatencyTuner(const LatencyConfig& config, +LatencyTuner::LatencyTuner(const LatencyConfig& latency_config, + const FreqEstimatorConfig& fe_config, const SampleSpec& sample_spec, dbgio::CsvDumper* dumper) : stream_pos_(0) - , scale_interval_(sample_spec.ns_2_stream_timestamp_delta(config.scaling_interval)) + , scale_interval_( + sample_spec.ns_2_stream_timestamp_delta(latency_config.scaling_interval)) , scale_pos_(0) , report_interval_(sample_spec.ns_2_stream_timestamp_delta(LogInterval)) , report_pos_(0) , has_new_freq_coeff_(false) , freq_coeff_(0) - , freq_coeff_max_delta_(config.scaling_tolerance) - , backend_(config.tuner_backend) - , profile_(config.tuner_profile) - , enable_latency_adjustment_(config.tuner_profile != LatencyTunerProfile_Intact) - , enable_tolerance_checks_(config.tuner_profile != LatencyTunerProfile_Intact - || config.target_latency != 0 - || config.start_target_latency != 0) - , latency_is_adaptive_(config.target_latency == 0) + , freq_coeff_max_delta_(latency_config.scaling_tolerance) + , backend_(latency_config.tuner_backend) + , profile_(latency_config.tuner_profile) + , enable_latency_adjustment_(latency_config.tuner_profile + != LatencyTunerProfile_Intact) + , enable_tolerance_checks_(latency_config.tuner_profile != LatencyTunerProfile_Intact + || latency_config.target_latency != 0 + || latency_config.start_target_latency != 0) + , latency_is_adaptive_(latency_config.target_latency == 0) , has_niq_latency_(false) , niq_latency_(0) , niq_stalling_(0) @@ -62,96 +65,97 @@ LatencyTuner::LatencyTuner(const LatencyConfig& config, , max_target_latency_(0) , min_actual_latency_(0) , max_actual_latency_(0) - , max_stalling_(sample_spec.ns_2_stream_timestamp_delta(config.stale_tolerance)) + , max_stalling_( + sample_spec.ns_2_stream_timestamp_delta(latency_config.stale_tolerance)) , sample_spec_(sample_spec) , target_latency_state_(TL_STARTING) - , starting_timeout_(config.starting_timeout) - , cooldown_dec_timeout_(config.cooldown_dec_timeout) - , cooldown_inc_timeout_(config.cooldown_inc_timeout) - , max_jitter_overhead_(config.max_jitter_overhead) - , mean_jitter_overhead_(config.mean_jitter_overhead) + , starting_timeout_(latency_config.starting_timeout) + , cooldown_dec_timeout_(latency_config.cooldown_dec_timeout) + , cooldown_inc_timeout_(latency_config.cooldown_inc_timeout) + , max_jitter_overhead_(latency_config.max_jitter_overhead) + , mean_jitter_overhead_(latency_config.mean_jitter_overhead) , last_target_latency_update_(0) - , lat_update_upper_thrsh_(config.latency_decrease_relative_threshold) - , lat_update_dec_step_( - upper_coef_to_step_lat_update_(config.latency_decrease_relative_threshold)) - , lat_update_inc_step_( - lower_thrs_to_step_lat_update_(config.latency_decrease_relative_threshold)) + , lat_update_upper_thrsh_(latency_config.latency_decrease_relative_threshold) + , lat_update_dec_step_(upper_coef_to_step_lat_update( + latency_config.latency_decrease_relative_threshold)) + , lat_update_inc_step_(lower_thrs_to_step_lat_update( + latency_config.latency_decrease_relative_threshold)) , last_lat_limiter_(LogInterval) , dumper_(dumper) , init_status_(status::NoStatus) { - roc_log(LogDebug, - "latency tuner: initializing:" - " backend=%s profile=%s tuning=%s" - " target_latency=%ld(%.3fms) latency_tolerance=%ld(%.3fms)" - " start_latency=%ld(%.3fms) min_latency=%ld(%.3fms) max_latency=%ld(%.3fms)" - " stale_tolerance=%ld(%.3fms)" - " scaling_interval=%ld(%.3fms) scaling_tolerance=%.3f", - // backend, profile, tuning - latency_tuner_backend_to_str(backend_), - latency_tuner_profile_to_str(profile_), - enable_latency_adjustment_ && latency_is_adaptive_ ? "adaptive" - : enable_latency_adjustment_ ? "fixed" - : "disabled", - // target_latency, latency_tolerance - (long)sample_spec_.ns_2_stream_timestamp_delta(config.target_latency), - (double)config.target_latency / core::Millisecond, - (long)sample_spec_.ns_2_stream_timestamp_delta(config.latency_tolerance), - (double)config.latency_tolerance / core::Millisecond, - // start_latency, min_latency, max_latency - (long)sample_spec_.ns_2_stream_timestamp_delta(config.start_target_latency), - (double)config.start_target_latency / core::Millisecond, - (long)sample_spec_.ns_2_stream_timestamp_delta(config.min_target_latency), - (double)config.min_target_latency / core::Millisecond, - (long)sample_spec_.ns_2_stream_timestamp_delta(config.max_target_latency), - (double)config.max_target_latency / core::Millisecond, - // stale_tolerance - (long)sample_spec_.ns_2_stream_timestamp_delta(config.stale_tolerance), - (double)config.stale_tolerance / core::Millisecond, - // scaling_interval, scaling_tolerance - (long)sample_spec_.ns_2_stream_timestamp_delta(config.scaling_interval), - (double)config.scaling_interval / core::Millisecond, - (double)config.scaling_tolerance); + roc_log( + LogDebug, + "latency tuner: initializing:" + " backend=%s profile=%s tuning=%s" + " target_latency=%ld(%.3fms) latency_tolerance=%ld(%.3fms)" + " start_latency=%ld(%.3fms) min_latency=%ld(%.3fms) max_latency=%ld(%.3fms)" + " stale_tolerance=%ld(%.3fms)" + " scaling_interval=%ld(%.3fms) scaling_tolerance=%.3f", + // backend, profile, tuning + latency_tuner_backend_to_str(backend_), latency_tuner_profile_to_str(profile_), + enable_latency_adjustment_ && latency_is_adaptive_ ? "adaptive" + : enable_latency_adjustment_ ? "fixed" + : "disabled", + // target_latency, latency_tolerance + (long)sample_spec_.ns_2_stream_timestamp_delta(latency_config.target_latency), + (double)latency_config.target_latency / core::Millisecond, + (long)sample_spec_.ns_2_stream_timestamp_delta(latency_config.latency_tolerance), + (double)latency_config.latency_tolerance / core::Millisecond, + // start_latency, min_latency, max_latency + (long)sample_spec_.ns_2_stream_timestamp_delta( + latency_config.start_target_latency), + (double)latency_config.start_target_latency / core::Millisecond, + (long)sample_spec_.ns_2_stream_timestamp_delta(latency_config.min_target_latency), + (double)latency_config.min_target_latency / core::Millisecond, + (long)sample_spec_.ns_2_stream_timestamp_delta(latency_config.max_target_latency), + (double)latency_config.max_target_latency / core::Millisecond, + // stale_tolerance + (long)sample_spec_.ns_2_stream_timestamp_delta(latency_config.stale_tolerance), + (double)latency_config.stale_tolerance / core::Millisecond, + // scaling_interval, scaling_tolerance + (long)sample_spec_.ns_2_stream_timestamp_delta(latency_config.scaling_interval), + (double)latency_config.scaling_interval / core::Millisecond, + (double)latency_config.scaling_tolerance); if (enable_latency_adjustment_ || enable_tolerance_checks_) { if (latency_is_adaptive_) { - roc_panic_if_msg( - config.target_latency != 0 || config.start_target_latency <= 0 - || config.min_target_latency < 0 || config.max_target_latency <= 0, - "latency tuner: invalid configuration"); + roc_panic_if_msg(latency_config.target_latency != 0 + || latency_config.start_target_latency <= 0 + || latency_config.min_target_latency < 0 + || latency_config.max_target_latency <= 0, + "latency tuner: invalid configuration"); - cur_target_latency_ = - sample_spec_.ns_2_stream_timestamp_delta(config.start_target_latency); + cur_target_latency_ = sample_spec_.ns_2_stream_timestamp_delta( + latency_config.start_target_latency); - min_target_latency_ = - sample_spec_.ns_2_stream_timestamp_delta(config.min_target_latency); - max_target_latency_ = - sample_spec_.ns_2_stream_timestamp_delta(config.max_target_latency); + min_target_latency_ = sample_spec_.ns_2_stream_timestamp_delta( + latency_config.min_target_latency); + max_target_latency_ = sample_spec_.ns_2_stream_timestamp_delta( + latency_config.max_target_latency); min_actual_latency_ = sample_spec_.ns_2_stream_timestamp_delta( - config.min_target_latency - config.latency_tolerance); + latency_config.min_target_latency - latency_config.latency_tolerance); max_actual_latency_ = sample_spec_.ns_2_stream_timestamp_delta( - config.max_target_latency + config.latency_tolerance); + latency_config.max_target_latency + latency_config.latency_tolerance); } else { - roc_panic_if_msg( - config.target_latency <= 0 || config.start_target_latency != 0 - || config.min_target_latency != 0 || config.max_target_latency != 0, - "latency tuner: invalid configuration"); + roc_panic_if_msg(latency_config.target_latency <= 0 + || latency_config.start_target_latency != 0 + || latency_config.min_target_latency != 0 + || latency_config.max_target_latency != 0, + "latency tuner: invalid configuration"); cur_target_latency_ = - sample_spec_.ns_2_stream_timestamp_delta(config.target_latency); + sample_spec_.ns_2_stream_timestamp_delta(latency_config.target_latency); min_actual_latency_ = sample_spec_.ns_2_stream_timestamp_delta( - config.target_latency - config.latency_tolerance); + latency_config.target_latency - latency_config.latency_tolerance); max_actual_latency_ = sample_spec_.ns_2_stream_timestamp_delta( - config.target_latency + config.latency_tolerance); + latency_config.target_latency + latency_config.latency_tolerance); } if (enable_latency_adjustment_) { fe_.reset(new (fe_) FreqEstimator( - profile_ == LatencyTunerProfile_Responsive - ? FreqEstimatorProfile_Responsive - : FreqEstimatorProfile_Gradual, - (packet::stream_timestamp_t)cur_target_latency_, dumper_)); + fe_config, (packet::stream_timestamp_t)cur_target_latency_, dumper_)); } } diff --git a/src/internal_modules/roc_audio/latency_tuner.h b/src/internal_modules/roc_audio/latency_tuner.h index 11bfbea86..67adba8cf 100644 --- a/src/internal_modules/roc_audio/latency_tuner.h +++ b/src/internal_modules/roc_audio/latency_tuner.h @@ -48,7 +48,8 @@ namespace audio { class LatencyTuner : public core::NonCopyable<> { public: //! Initialize. - LatencyTuner(const LatencyConfig& config, + LatencyTuner(const LatencyConfig& latency_config, + const FreqEstimatorConfig& fe_config, const SampleSpec& sample_spec, dbgio::CsvDumper* dumper); diff --git a/src/internal_modules/roc_audio/resampler_config.cpp b/src/internal_modules/roc_audio/resampler_config.cpp index 3d966c804..3ac10eef0 100644 --- a/src/internal_modules/roc_audio/resampler_config.cpp +++ b/src/internal_modules/roc_audio/resampler_config.cpp @@ -17,15 +17,16 @@ bool ResamplerConfig::deduce_defaults(ProcessorMap& processor_map, LatencyTunerProfile latency_profile) { if (backend == ResamplerBackend_Auto) { // If responsive profile is set, use builtin backend instead of speex, - // since it has higher scaling precision. - const bool need_builtin_backend = - latency_profile == LatencyTunerProfile_Responsive; + // since it has higher scaling precision. Same applies to E2E backend. + const bool prefer_builtin_resampler = latency_backend == LatencyTunerBackend_E2e + || latency_profile == LatencyTunerProfile_Responsive; - // If speex backend is not available, fallback to builtin backend. + // Even if we don't require builtin resampler, if speex backend is not available, + // we fallback to builtin just because it's always available. const bool force_builtin_backend = !processor_map.has_resampler_backend(ResamplerBackend_Speex); - if (need_builtin_backend || force_builtin_backend) { + if (prefer_builtin_resampler || force_builtin_backend) { backend = ResamplerBackend_Builtin; } else { backend = ResamplerBackend_Speex; diff --git a/src/internal_modules/roc_pipeline/config.cpp b/src/internal_modules/roc_pipeline/config.cpp index b0d229b37..9594cedae 100644 --- a/src/internal_modules/roc_pipeline/config.cpp +++ b/src/internal_modules/roc_pipeline/config.cpp @@ -29,6 +29,14 @@ bool SenderSinkConfig::deduce_defaults(audio::ProcessorMap& processor_map) { return false; } + if (!link_meter.deduce_defaults(latency.tuner_profile)) { + return false; + } + + if (!freq_est.deduce_defaults(latency.tuner_profile)) { + return false; + } + if (!resampler.deduce_defaults(processor_map, latency.tuner_backend, latency.tuner_profile)) { return false; @@ -74,7 +82,11 @@ bool ReceiverSessionConfig::deduce_defaults(audio::ProcessorMap& processor_map) return false; } - if (!watchdog.deduce_defaults(DefaultLatency, latency.target_latency)) { + if (!link_meter.deduce_defaults(latency.tuner_profile)) { + return false; + } + + if (!freq_est.deduce_defaults(latency.tuner_profile)) { return false; } @@ -83,6 +95,10 @@ bool ReceiverSessionConfig::deduce_defaults(audio::ProcessorMap& processor_map) return false; } + if (!watchdog.deduce_defaults(DefaultLatency, latency.target_latency)) { + return false; + } + return true; } diff --git a/src/internal_modules/roc_pipeline/config.h b/src/internal_modules/roc_pipeline/config.h index df2eccb62..bb79b32ee 100644 --- a/src/internal_modules/roc_pipeline/config.h +++ b/src/internal_modules/roc_pipeline/config.h @@ -31,6 +31,7 @@ #include "roc_pipeline/pipeline_loop.h" #include "roc_rtcp/config.h" #include "roc_rtp/filter.h" +#include "roc_rtp/link_meter.h" namespace roc { namespace pipeline { @@ -77,11 +78,17 @@ struct SenderSinkConfig { //! FEC encoder parameters. fec::CodecConfig fec_encoder; + //! Feedback parameters. + audio::FeedbackConfig feedback; + //! Latency parameters. audio::LatencyConfig latency; - //! Feedback parameters. - audio::FeedbackConfig feedback; + //! Link meter parameters. + rtp::LinkMeterConfig link_meter; + + //! Freq estimator parameters. + audio::FreqEstimatorConfig freq_est; //! Resampler parameters. audio::ResamplerConfig resampler; @@ -173,12 +180,18 @@ struct ReceiverSessionConfig { //! Latency parameters. audio::LatencyConfig latency; - //! Watchdog parameters. - audio::WatchdogConfig watchdog; + //! Link meter parameters. + rtp::LinkMeterConfig link_meter; + + //! Freq estimator parameters. + audio::FreqEstimatorConfig freq_est; //! Resampler parameters. audio::ResamplerConfig resampler; + //! Watchdog parameters. + audio::WatchdogConfig watchdog; + //! Initialize config. ReceiverSessionConfig(); diff --git a/src/internal_modules/roc_pipeline/receiver_session.cpp b/src/internal_modules/roc_pipeline/receiver_session.cpp index 5249eff1c..f2ab3daf8 100644 --- a/src/internal_modules/roc_pipeline/receiver_session.cpp +++ b/src/internal_modules/roc_pipeline/receiver_session.cpp @@ -54,7 +54,7 @@ ReceiverSession::ReceiverSession(const ReceiverSessionConfig& session_config, pkt_writer = source_queue_.get(); source_meter_.reset(new (source_meter_) rtp::LinkMeter( - *pkt_writer, session_config.latency, encoding_map, arena, dumper_)); + *pkt_writer, session_config.link_meter, encoding_map, arena, dumper_)); if ((init_status_ = source_meter_->init_status()) != status::StatusOK) { return; } @@ -110,7 +110,7 @@ ReceiverSession::ReceiverSession(const ReceiverSessionConfig& session_config, repair_pkt_writer = repair_queue_.get(); repair_meter_.reset(new (repair_meter_) rtp::LinkMeter( - *repair_pkt_writer, session_config.latency, encoding_map, arena, dumper_)); + *repair_pkt_writer, session_config.link_meter, encoding_map, arena, dumper_)); if ((init_status_ = repair_meter_->init_status()) != status::StatusOK) { return; } @@ -267,7 +267,7 @@ ReceiverSession::ReceiverSession(const ReceiverSessionConfig& session_config, latency_monitor_.reset(new (latency_monitor_) audio::LatencyMonitor( *frm_reader, *source_queue_, *depacketizer_, *source_meter_, fec_reader_.get(), resampler_reader_.get(), session_config.latency, - pkt_encoding->sample_spec, inout_spec, dumper_)); + session_config.freq_est, pkt_encoding->sample_spec, inout_spec, dumper_)); if ((init_status_ = latency_monitor_->init_status()) != status::StatusOK) { return; } diff --git a/src/internal_modules/roc_pipeline/sender_session.cpp b/src/internal_modules/roc_pipeline/sender_session.cpp index d66be0495..d59589b4f 100644 --- a/src/internal_modules/roc_pipeline/sender_session.cpp +++ b/src/internal_modules/roc_pipeline/sender_session.cpp @@ -213,7 +213,7 @@ SenderSession::create_transport_pipeline(SenderEndpoint* source_endpoint, feedback_monitor_.reset(new (feedback_monitor_) audio::FeedbackMonitor( *frm_writer, *packetizer_, resampler_writer_.get(), sink_config_.feedback, - sink_config_.latency, inout_spec, dumper_)); + sink_config_.latency, sink_config_.freq_est, inout_spec, dumper_)); if ((status = feedback_monitor_->init_status()) != status::StatusOK) { return status; } diff --git a/src/internal_modules/roc_rtp/link_meter.cpp b/src/internal_modules/roc_rtp/link_meter.cpp index d2c169711..822980904 100644 --- a/src/internal_modules/roc_rtp/link_meter.cpp +++ b/src/internal_modules/roc_rtp/link_meter.cpp @@ -13,8 +13,21 @@ namespace roc { namespace rtp { +bool LinkMeterConfig::deduce_defaults(audio::LatencyTunerProfile latency_profile) { + if (sliding_window_length == 0) { + if (latency_profile == audio::LatencyTunerProfile_Responsive) { + // Responsive profile requires faster reactions to network changes. + sliding_window_length = 10000; + } else { + sliding_window_length = 30000; + } + } + + return true; +} + LinkMeter::LinkMeter(packet::IWriter& writer, - const audio::LatencyConfig& latency_config, + const LinkMeterConfig& config, const EncodingMap& encoding_map, core::IArena& arena, dbgio::CsvDumper* dumper) @@ -22,7 +35,7 @@ LinkMeter::LinkMeter(packet::IWriter& writer, , encoding_(NULL) , writer_(writer) , first_packet_(true) - , win_len_(latency_config.sliding_window_length) + , win_len_(config.sliding_window_length) , has_metrics_(false) , first_seqnum_(0) , last_seqnum_hi_(0) diff --git a/src/internal_modules/roc_rtp/link_meter.h b/src/internal_modules/roc_rtp/link_meter.h index 1202c7ee4..34d3e91ce 100644 --- a/src/internal_modules/roc_rtp/link_meter.h +++ b/src/internal_modules/roc_rtp/link_meter.h @@ -28,6 +28,21 @@ namespace roc { namespace rtp { +//! RTP link meter parameters. +struct LinkMeterConfig { + //! Number of packets we use to calculate sliding statistics. + //! @remarks + //! We calculate jitter statistics based on this last delivered packets. + size_t sliding_window_length; + + LinkMeterConfig() + : sliding_window_length(0) { + } + + //! Automatically fill missing settings. + ROC_ATTR_NODISCARD bool deduce_defaults(audio::LatencyTunerProfile latency_profile); +}; + //! RTP link meter. //! //! Computes various link metrics based on sequence of RTP packets. @@ -41,7 +56,7 @@ class LinkMeter : public packet::ILinkMeter, public: //! Initialize. LinkMeter(packet::IWriter& writer, - const audio::LatencyConfig& latency_config, + const LinkMeterConfig& config, const EncodingMap& encoding_map, core::IArena& arena, dbgio::CsvDumper* dumper); diff --git a/src/tests/roc_audio/test_freq_estimator.cpp b/src/tests/roc_audio/test_freq_estimator.cpp index 25176fe84..8df79d93b 100644 --- a/src/tests/roc_audio/test_freq_estimator.cpp +++ b/src/tests/roc_audio/test_freq_estimator.cpp @@ -18,28 +18,32 @@ namespace { enum { Target = 10000 }; -const FreqEstimatorProfile Profiles[] = { FreqEstimatorProfile_Responsive, - FreqEstimatorProfile_Gradual }; +const LatencyTunerProfile profile_list[] = { LatencyTunerProfile_Responsive, + LatencyTunerProfile_Gradual }; const double Epsilon = 0.0001; } // namespace -TEST_GROUP(freq_estimator) { - FreqEstimatorConfig fe_config; -}; +TEST_GROUP(freq_estimator) {}; TEST(freq_estimator, initial) { - for (size_t p = 0; p < ROC_ARRAY_SIZE(Profiles); p++) { - FreqEstimator fe(Profiles[p], Target, NULL); + for (size_t p = 0; p < ROC_ARRAY_SIZE(profile_list); p++) { + FreqEstimatorConfig config; + CHECK(config.deduce_defaults(profile_list[p])); + + FreqEstimator fe(config, Target, NULL); DOUBLES_EQUAL(1.0, (double)fe.freq_coeff(), Epsilon); } } TEST(freq_estimator, aim_queue_size) { - for (size_t p = 0; p < ROC_ARRAY_SIZE(Profiles); p++) { - FreqEstimator fe(Profiles[p], Target, NULL); + for (size_t p = 0; p < ROC_ARRAY_SIZE(profile_list); p++) { + FreqEstimatorConfig config; + CHECK(config.deduce_defaults(profile_list[p])); + + FreqEstimator fe(config, Target, NULL); for (size_t n = 0; n < 1000; n++) { fe.update_current_latency(Target); @@ -50,8 +54,11 @@ TEST(freq_estimator, aim_queue_size) { } TEST(freq_estimator, large_queue_size) { - for (size_t p = 0; p < ROC_ARRAY_SIZE(Profiles); p++) { - FreqEstimator fe(Profiles[p], Target, NULL); + for (size_t p = 0; p < ROC_ARRAY_SIZE(profile_list); p++) { + FreqEstimatorConfig config; + CHECK(config.deduce_defaults(profile_list[p])); + + FreqEstimator fe(config, Target, NULL); do { fe.update_current_latency(Target * 2); @@ -60,8 +67,11 @@ TEST(freq_estimator, large_queue_size) { } TEST(freq_estimator, small_queue_size) { - for (size_t p = 0; p < ROC_ARRAY_SIZE(Profiles); p++) { - FreqEstimator fe(Profiles[p], Target, NULL); + for (size_t p = 0; p < ROC_ARRAY_SIZE(profile_list); p++) { + FreqEstimatorConfig config; + CHECK(config.deduce_defaults(profile_list[p])); + + FreqEstimator fe(config, Target, NULL); do { fe.update_current_latency(Target / 2); diff --git a/src/tests/roc_rtp/test_link_meter.cpp b/src/tests/roc_rtp/test_link_meter.cpp index 695dcc9aa..2d4bb6379 100644 --- a/src/tests/roc_rtp/test_link_meter.cpp +++ b/src/tests/roc_rtp/test_link_meter.cpp @@ -61,11 +61,10 @@ packet::PacketPtr new_packet(packet::seqnum_t sn, return packet; } -audio::LatencyConfig make_config() { - audio::LatencyConfig latency_config; - latency_config.tuner_profile = audio::LatencyTunerProfile_Responsive; - CHECK(latency_config.deduce_defaults(pipeline::DefaultLatency, true)); - return latency_config; +LinkMeterConfig make_config() { + LinkMeterConfig config; + config.sliding_window_length = 10000; + return config; } } // namespace