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