diff options
Diffstat (limited to 'modules/audio_processing/aec3')
6 files changed, 285 insertions, 65 deletions
diff --git a/modules/audio_processing/aec3/echo_canceller3.cc b/modules/audio_processing/aec3/echo_canceller3.cc index a74c37e0c9..992e295dfb 100644 --- a/modules/audio_processing/aec3/echo_canceller3.cc +++ b/modules/audio_processing/aec3/echo_canceller3.cc @@ -704,7 +704,9 @@ EchoCanceller3::EchoCanceller3( config_selector_.active_config() .multi_channel.stereo_detection_threshold, config_selector_.active_config() - .multi_channel.stereo_detection_timeout_threshold_seconds), + .multi_channel.stereo_detection_timeout_threshold_seconds, + config_selector_.active_config() + .multi_channel.stereo_detection_hysteresis_seconds), output_framer_(num_bands_, num_capture_channels_), capture_blocker_(num_bands_, num_capture_channels_), render_transfer_queue_( @@ -772,12 +774,12 @@ void EchoCanceller3::Initialize() { RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_); num_render_channels_to_aec_ = - multichannel_content_detector_.IsMultiChannelContentDetected() + multichannel_content_detector_.IsProperMultiChannelContentDetected() ? num_render_input_channels_ : 1; config_selector_.Update( - multichannel_content_detector_.IsMultiChannelContentDetected()); + multichannel_content_detector_.IsProperMultiChannelContentDetected()); for (std::vector<std::vector<float>>& block_band : render_block_) { block_band.resize(num_render_channels_to_aec_); diff --git a/modules/audio_processing/aec3/echo_canceller3.h b/modules/audio_processing/aec3/echo_canceller3.h index ba5895f34a..831a7c738a 100644 --- a/modules/audio_processing/aec3/echo_canceller3.h +++ b/modules/audio_processing/aec3/echo_canceller3.h @@ -144,6 +144,8 @@ class EchoCanceller3 : public EchoControl { FRIEND_TEST_ALL_PREFIXES(EchoCanceller3, DetectionOfProperStereoUsingThreshold); FRIEND_TEST_ALL_PREFIXES(EchoCanceller3, + DetectionOfProperStereoUsingHysteresis); + FRIEND_TEST_ALL_PREFIXES(EchoCanceller3, StereoContentDetectionForMonoSignals); class RenderWriter; @@ -158,7 +160,7 @@ class EchoCanceller3 : public EchoControl { // Only for testing. Returns whether stereo processing is active. bool StereoRenderProcessingActiveForTesting() const { - return multichannel_content_detector_.IsMultiChannelContentDetected(); + return multichannel_content_detector_.IsProperMultiChannelContentDetected(); } // Only for testing. diff --git a/modules/audio_processing/aec3/echo_canceller3_unittest.cc b/modules/audio_processing/aec3/echo_canceller3_unittest.cc index 81cefb6f92..9a2df48dde 100644 --- a/modules/audio_processing/aec3/echo_canceller3_unittest.cc +++ b/modules/audio_processing/aec3/echo_canceller3_unittest.cc @@ -947,6 +947,7 @@ TEST(EchoCanceller3, DetectionOfProperStereo) { mono_config.multi_channel.detect_stereo_content = true; mono_config.multi_channel.stereo_detection_threshold = 0.0f; + mono_config.multi_channel.stereo_detection_hysteresis_seconds = 0.0f; multichannel_config = mono_config; mono_config.filter.coarse_initial.length_blocks = kNumBlocksForMonoConfig; multichannel_config->filter.coarse_initial.length_blocks = @@ -994,6 +995,7 @@ TEST(EchoCanceller3, DetectionOfProperStereoUsingThreshold) { mono_config.multi_channel.detect_stereo_content = true; mono_config.multi_channel.stereo_detection_threshold = kStereoDetectionThreshold; + mono_config.multi_channel.stereo_detection_hysteresis_seconds = 0.0f; multichannel_config = mono_config; mono_config.filter.coarse_initial.length_blocks = kNumBlocksForMonoConfig; multichannel_config->filter.coarse_initial.length_blocks = @@ -1024,6 +1026,64 @@ TEST(EchoCanceller3, DetectionOfProperStereoUsingThreshold) { kNumBlocksForSurroundConfig); } +TEST(EchoCanceller3, DetectionOfProperStereoUsingHysteresis) { + constexpr int kSampleRateHz = 16000; + constexpr int kNumChannels = 2; + AudioBuffer buffer(/*input_rate=*/kSampleRateHz, + /*input_num_channels=*/kNumChannels, + /*input_rate=*/kSampleRateHz, + /*buffer_num_channels=*/kNumChannels, + /*output_rate=*/kSampleRateHz, + /*output_num_channels=*/kNumChannels); + + constexpr size_t kNumBlocksForMonoConfig = 1; + constexpr size_t kNumBlocksForSurroundConfig = 2; + EchoCanceller3Config mono_config; + absl::optional<EchoCanceller3Config> surround_config; + + mono_config.multi_channel.detect_stereo_content = true; + mono_config.multi_channel.stereo_detection_hysteresis_seconds = 0.5f; + surround_config = mono_config; + mono_config.filter.coarse_initial.length_blocks = kNumBlocksForMonoConfig; + surround_config->filter.coarse_initial.length_blocks = + kNumBlocksForSurroundConfig; + + EchoCanceller3 aec3(mono_config, surround_config, + /*sample_rate_hz=*/kSampleRateHz, + /*num_render_channels=*/kNumChannels, + /*num_capture_input_channels=*/kNumChannels); + + EXPECT_FALSE(aec3.StereoRenderProcessingActiveForTesting()); + EXPECT_EQ( + aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks, + kNumBlocksForMonoConfig); + + RunAecInStereo(buffer, aec3, 100.0f, 100.0f); + EXPECT_FALSE(aec3.StereoRenderProcessingActiveForTesting()); + EXPECT_EQ( + aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks, + kNumBlocksForMonoConfig); + + constexpr int kNumFramesPerSecond = 100; + for (int k = 0; + k < static_cast<int>( + kNumFramesPerSecond * + mono_config.multi_channel.stereo_detection_hysteresis_seconds); + ++k) { + RunAecInStereo(buffer, aec3, 100.0f, 101.0f); + EXPECT_FALSE(aec3.StereoRenderProcessingActiveForTesting()); + EXPECT_EQ( + aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks, + kNumBlocksForMonoConfig); + } + + RunAecInStereo(buffer, aec3, 100.0f, 101.0f); + EXPECT_TRUE(aec3.StereoRenderProcessingActiveForTesting()); + EXPECT_EQ( + aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks, + kNumBlocksForSurroundConfig); +} + TEST(EchoCanceller3, StereoContentDetectionForMonoSignals) { constexpr int kSampleRateHz = 16000; constexpr int kNumChannels = 2; diff --git a/modules/audio_processing/aec3/multi_channel_content_detector.cc b/modules/audio_processing/aec3/multi_channel_content_detector.cc index 270316ebc1..8d1bd9108c 100644 --- a/modules/audio_processing/aec3/multi_channel_content_detector.cc +++ b/modules/audio_processing/aec3/multi_channel_content_detector.cc @@ -13,6 +13,8 @@ #include <cmath> +#include "rtc_base/checks.h" + namespace webrtc { namespace { @@ -23,8 +25,8 @@ constexpr int kNumFramesPerSecond = 100; // whether the signal is a proper stereo signal. To allow for differences // introduced by hardware drivers, a threshold `detection_threshold` is used for // the detection. -bool IsProperStereo(const std::vector<std::vector<std::vector<float>>>& frame, - float detection_threshold) { +bool HasStereoContent(const std::vector<std::vector<std::vector<float>>>& frame, + float detection_threshold) { if (frame[0].size() < 2) { return false; } @@ -46,7 +48,8 @@ MultiChannelContentDetector::MultiChannelContentDetector( bool detect_stereo_content, int num_render_input_channels, float detection_threshold, - int stereo_detection_timeout_threshold_seconds) + int stereo_detection_timeout_threshold_seconds, + float stereo_detection_hysteresis_seconds) : detect_stereo_content_(detect_stereo_content), detection_threshold_(detection_threshold), detection_timeout_threshold_frames_( @@ -54,29 +57,46 @@ MultiChannelContentDetector::MultiChannelContentDetector( ? absl::make_optional(stereo_detection_timeout_threshold_seconds * kNumFramesPerSecond) : absl::nullopt), - proper_multichannel_content_detected_(!detect_stereo_content && - num_render_input_channels > 1) {} + stereo_detection_hysteresis_frames_(static_cast<int>( + stereo_detection_hysteresis_seconds * kNumFramesPerSecond)), + persistent_multichannel_content_detected_( + !detect_stereo_content && num_render_input_channels > 1) {} bool MultiChannelContentDetector::UpdateDetection( const std::vector<std::vector<std::vector<float>>>& frame) { - if (!detect_stereo_content_) + if (!detect_stereo_content_) { + RTC_DCHECK_EQ(frame[0].size() > 1, + persistent_multichannel_content_detected_); return false; + } - const bool previous_proper_multichannel_content_detected = - proper_multichannel_content_detected_; - - if (IsProperStereo(frame, detection_threshold_)) { - proper_multichannel_content_detected_ = true; - frames_since_stereo_detected_ = 0; - } else { - ++frames_since_stereo_detected_; - if (detection_timeout_threshold_frames_ && - frames_since_stereo_detected_ >= *detection_timeout_threshold_frames_) { - proper_multichannel_content_detected_ = false; - } + const bool previous_persistent_multichannel_content_detected = + persistent_multichannel_content_detected_; + const bool stereo_detected_in_frame = + HasStereoContent(frame, detection_threshold_); + + consecutive_frames_with_stereo_ = + stereo_detected_in_frame ? consecutive_frames_with_stereo_ + 1 : 0; + frames_since_stereo_detected_last_ = + stereo_detected_in_frame ? 0 : frames_since_stereo_detected_last_ + 1; + + // Detect persistent multichannel content. + if (consecutive_frames_with_stereo_ > stereo_detection_hysteresis_frames_) { + persistent_multichannel_content_detected_ = true; + } + if (detection_timeout_threshold_frames_.has_value() && + frames_since_stereo_detected_last_ >= + *detection_timeout_threshold_frames_) { + persistent_multichannel_content_detected_ = false; } - return previous_proper_multichannel_content_detected != - proper_multichannel_content_detected_; + + // Detect temporary multichannel content. + temporary_multichannel_content_detected_ = + persistent_multichannel_content_detected_ ? false + : stereo_detected_in_frame; + + return previous_persistent_multichannel_content_detected != + persistent_multichannel_content_detected_; } } // namespace webrtc diff --git a/modules/audio_processing/aec3/multi_channel_content_detector.h b/modules/audio_processing/aec3/multi_channel_content_detector.h index e4f3f09ba1..3120502258 100644 --- a/modules/audio_processing/aec3/multi_channel_content_detector.h +++ b/modules/audio_processing/aec3/multi_channel_content_detector.h @@ -31,24 +31,33 @@ class MultiChannelContentDetector { MultiChannelContentDetector(bool detect_stereo_content, int num_render_input_channels, float detection_threshold, - int stereo_detection_timeout_threshold_seconds); + int stereo_detection_timeout_threshold_seconds, + float stereo_detection_hysteresis_seconds); // Compares the left and right channels in the render `frame` to determine // whether the signal is a proper multichannel signal. Returns a bool - // indicating whether a change in the multichannel was detected. + // indicating whether a change in the proper multichannel content was + // detected. bool UpdateDetection( const std::vector<std::vector<std::vector<float>>>& frame); - bool IsMultiChannelContentDetected() const { - return proper_multichannel_content_detected_; + bool IsProperMultiChannelContentDetected() const { + return persistent_multichannel_content_detected_; + } + + bool IsTemporaryMultiChannelContentDetectedForTesting() const { + return temporary_multichannel_content_detected_; } private: const bool detect_stereo_content_; const float detection_threshold_; const absl::optional<int> detection_timeout_threshold_frames_; - bool proper_multichannel_content_detected_; - int frames_since_stereo_detected_ = 0; + const int stereo_detection_hysteresis_frames_; + bool persistent_multichannel_content_detected_; + bool temporary_multichannel_content_detected_ = false; + int64_t frames_since_stereo_detected_last_ = 0; + int64_t consecutive_frames_with_stereo_ = 0; }; } // namespace webrtc diff --git a/modules/audio_processing/aec3/multi_channel_content_detector_unittest.cc b/modules/audio_processing/aec3/multi_channel_content_detector_unittest.cc index cb495e859f..0857bee4bd 100644 --- a/modules/audio_processing/aec3/multi_channel_content_detector_unittest.cc +++ b/modules/audio_processing/aec3/multi_channel_content_detector_unittest.cc @@ -19,8 +19,9 @@ TEST(MultiChannelContentDetector, HandlingOfMono) { /*detect_stereo_content=*/true, /*num_render_input_channels=*/1, /*detection_threshold=*/0.0f, - /*stereo_detection_timeout_threshold_seconds=*/0); - EXPECT_FALSE(mc.IsMultiChannelContentDetected()); + /*stereo_detection_timeout_threshold_seconds=*/0, + /*stereo_detection_hysteresis_seconds=*/0.0f); + EXPECT_FALSE(mc.IsProperMultiChannelContentDetected()); } TEST(MultiChannelContentDetector, HandlingOfMonoAndDetectionOff) { @@ -28,8 +29,9 @@ TEST(MultiChannelContentDetector, HandlingOfMonoAndDetectionOff) { /*detect_stereo_content=*/false, /*num_render_input_channels=*/1, /*detection_threshold=*/0.0f, - /*stereo_detection_timeout_threshold_seconds=*/0); - EXPECT_FALSE(mc.IsMultiChannelContentDetected()); + /*stereo_detection_timeout_threshold_seconds=*/0, + /*stereo_detection_hysteresis_seconds=*/0.0f); + EXPECT_FALSE(mc.IsProperMultiChannelContentDetected()); } TEST(MultiChannelContentDetector, HandlingOfDetectionOff) { @@ -37,8 +39,9 @@ TEST(MultiChannelContentDetector, HandlingOfDetectionOff) { /*detect_stereo_content=*/false, /*num_render_input_channels=*/2, /*detection_threshold=*/0.0f, - /*stereo_detection_timeout_threshold_seconds=*/0); - EXPECT_TRUE(mc.IsMultiChannelContentDetected()); + /*stereo_detection_timeout_threshold_seconds=*/0, + /*stereo_detection_hysteresis_seconds=*/0.0f); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); std::vector<std::vector<std::vector<float>>> frame( 1, std::vector<std::vector<float>>(2, std::vector<float>(160, 0.0f))); @@ -46,7 +49,7 @@ TEST(MultiChannelContentDetector, HandlingOfDetectionOff) { std::fill(frame[0][1].begin(), frame[0][1].end(), 101.0f); EXPECT_FALSE(mc.UpdateDetection(frame)); - EXPECT_TRUE(mc.IsMultiChannelContentDetected()); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); EXPECT_FALSE(mc.UpdateDetection(frame)); } @@ -56,8 +59,9 @@ TEST(MultiChannelContentDetector, InitialDetectionOfStereo) { /*detect_stereo_content=*/true, /*num_render_input_channels=*/2, /*detection_threshold=*/0.0f, - /*stereo_detection_timeout_threshold_seconds=*/0); - EXPECT_FALSE(mc.IsMultiChannelContentDetected()); + /*stereo_detection_timeout_threshold_seconds=*/0, + /*stereo_detection_hysteresis_seconds=*/0.0f); + EXPECT_FALSE(mc.IsProperMultiChannelContentDetected()); } TEST(MultiChannelContentDetector, DetectionWhenFakeStereo) { @@ -65,13 +69,14 @@ TEST(MultiChannelContentDetector, DetectionWhenFakeStereo) { /*detect_stereo_content=*/true, /*num_render_input_channels=*/2, /*detection_threshold=*/0.0f, - /*stereo_detection_timeout_threshold_seconds=*/0); + /*stereo_detection_timeout_threshold_seconds=*/0, + /*stereo_detection_hysteresis_seconds=*/0.0f); std::vector<std::vector<std::vector<float>>> frame( 1, std::vector<std::vector<float>>(2, std::vector<float>(160, 0.0f))); std::fill(frame[0][0].begin(), frame[0][0].end(), 100.0f); std::fill(frame[0][1].begin(), frame[0][1].end(), 100.0f); EXPECT_FALSE(mc.UpdateDetection(frame)); - EXPECT_FALSE(mc.IsMultiChannelContentDetected()); + EXPECT_FALSE(mc.IsProperMultiChannelContentDetected()); EXPECT_FALSE(mc.UpdateDetection(frame)); } @@ -81,13 +86,14 @@ TEST(MultiChannelContentDetector, DetectionWhenStereo) { /*detect_stereo_content=*/true, /*num_render_input_channels=*/2, /*detection_threshold=*/0.0f, - /*stereo_detection_timeout_threshold_seconds=*/0); + /*stereo_detection_timeout_threshold_seconds=*/0, + /*stereo_detection_hysteresis_seconds=*/0.0f); std::vector<std::vector<std::vector<float>>> frame( 1, std::vector<std::vector<float>>(2, std::vector<float>(160, 0.0f))); std::fill(frame[0][0].begin(), frame[0][0].end(), 100.0f); std::fill(frame[0][1].begin(), frame[0][1].end(), 101.0f); EXPECT_TRUE(mc.UpdateDetection(frame)); - EXPECT_TRUE(mc.IsMultiChannelContentDetected()); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); EXPECT_FALSE(mc.UpdateDetection(frame)); } @@ -97,14 +103,15 @@ TEST(MultiChannelContentDetector, DetectionWhenStereoAfterAWhile) { /*detect_stereo_content=*/true, /*num_render_input_channels=*/2, /*detection_threshold=*/0.0f, - /*stereo_detection_timeout_threshold_seconds=*/0); + /*stereo_detection_timeout_threshold_seconds=*/0, + /*stereo_detection_hysteresis_seconds=*/0.0f); std::vector<std::vector<std::vector<float>>> frame( 1, std::vector<std::vector<float>>(2, std::vector<float>(160, 0.0f))); std::fill(frame[0][0].begin(), frame[0][0].end(), 100.0f); std::fill(frame[0][1].begin(), frame[0][1].end(), 100.0f); EXPECT_FALSE(mc.UpdateDetection(frame)); - EXPECT_FALSE(mc.IsMultiChannelContentDetected()); + EXPECT_FALSE(mc.IsProperMultiChannelContentDetected()); EXPECT_FALSE(mc.UpdateDetection(frame)); @@ -112,7 +119,7 @@ TEST(MultiChannelContentDetector, DetectionWhenStereoAfterAWhile) { std::fill(frame[0][1].begin(), frame[0][1].end(), 101.0f); EXPECT_TRUE(mc.UpdateDetection(frame)); - EXPECT_TRUE(mc.IsMultiChannelContentDetected()); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); EXPECT_FALSE(mc.UpdateDetection(frame)); } @@ -123,14 +130,15 @@ TEST(MultiChannelContentDetector, DetectionWithStereoBelowThreshold) { /*detect_stereo_content=*/true, /*num_render_input_channels=*/2, /*detection_threshold=*/kThreshold, - /*stereo_detection_timeout_threshold_seconds=*/0); + /*stereo_detection_timeout_threshold_seconds=*/0, + /*stereo_detection_hysteresis_seconds=*/0.0f); std::vector<std::vector<std::vector<float>>> frame( 1, std::vector<std::vector<float>>(2, std::vector<float>(160, 0.0f))); std::fill(frame[0][0].begin(), frame[0][0].end(), 100.0f); std::fill(frame[0][1].begin(), frame[0][1].end(), 100.0f + kThreshold); EXPECT_FALSE(mc.UpdateDetection(frame)); - EXPECT_FALSE(mc.IsMultiChannelContentDetected()); + EXPECT_FALSE(mc.IsProperMultiChannelContentDetected()); EXPECT_FALSE(mc.UpdateDetection(frame)); } @@ -141,14 +149,15 @@ TEST(MultiChannelContentDetector, DetectionWithStereoAboveThreshold) { /*detect_stereo_content=*/true, /*num_render_input_channels=*/2, /*detection_threshold=*/kThreshold, - /*stereo_detection_timeout_threshold_seconds=*/0); + /*stereo_detection_timeout_threshold_seconds=*/0, + /*stereo_detection_hysteresis_seconds=*/0.0f); std::vector<std::vector<std::vector<float>>> frame( 1, std::vector<std::vector<float>>(2, std::vector<float>(160, 0.0f))); std::fill(frame[0][0].begin(), frame[0][0].end(), 100.0f); std::fill(frame[0][1].begin(), frame[0][1].end(), 100.0f + kThreshold + 0.1f); EXPECT_TRUE(mc.UpdateDetection(frame)); - EXPECT_TRUE(mc.IsMultiChannelContentDetected()); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); EXPECT_FALSE(mc.UpdateDetection(frame)); } @@ -166,16 +175,16 @@ TEST_P(MultiChannelContentDetectorTimeoutBehavior, TimeOutBehaviorForNonTrueStereo) { constexpr int kNumFramesPerSecond = 100; const bool detect_stereo_content = std::get<0>(GetParam()); - const int stereo_stereo_detection_timeout_threshold_seconds = + const int stereo_detection_timeout_threshold_seconds = std::get<1>(GetParam()); const int stereo_detection_timeout_threshold_frames = - stereo_stereo_detection_timeout_threshold_seconds * kNumFramesPerSecond; + stereo_detection_timeout_threshold_seconds * kNumFramesPerSecond; - MultiChannelContentDetector mc( - detect_stereo_content, - /*num_render_input_channels=*/2, - /*detection_threshold=*/0.0f, - stereo_stereo_detection_timeout_threshold_seconds); + MultiChannelContentDetector mc(detect_stereo_content, + /*num_render_input_channels=*/2, + /*detection_threshold=*/0.0f, + stereo_detection_timeout_threshold_seconds, + /*stereo_detection_hysteresis_seconds=*/0.0f); std::vector<std::vector<std::vector<float>>> true_stereo_frame = { {std::vector<float>(160, 100.0f), std::vector<float>(160, 101.0f)}}; @@ -186,9 +195,9 @@ TEST_P(MultiChannelContentDetectorTimeoutBehavior, for (int k = 0; k < 10; ++k) { EXPECT_FALSE(mc.UpdateDetection(fake_stereo_frame)); if (detect_stereo_content) { - EXPECT_FALSE(mc.IsMultiChannelContentDetected()); + EXPECT_FALSE(mc.IsProperMultiChannelContentDetected()); } else { - EXPECT_TRUE(mc.IsMultiChannelContentDetected()); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); } } @@ -198,21 +207,21 @@ TEST_P(MultiChannelContentDetectorTimeoutBehavior, } else { EXPECT_FALSE(mc.UpdateDetection(true_stereo_frame)); } - EXPECT_TRUE(mc.IsMultiChannelContentDetected()); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); // Pass fake stereo frames until any timeouts are about to occur. for (int k = 0; k < stereo_detection_timeout_threshold_frames - 1; ++k) { EXPECT_FALSE(mc.UpdateDetection(fake_stereo_frame)); - EXPECT_TRUE(mc.IsMultiChannelContentDetected()); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); } // Pass a fake stereo frame and verify that any timeouts properly occur. if (detect_stereo_content && stereo_detection_timeout_threshold_frames > 0) { EXPECT_TRUE(mc.UpdateDetection(fake_stereo_frame)); - EXPECT_FALSE(mc.IsMultiChannelContentDetected()); + EXPECT_FALSE(mc.IsProperMultiChannelContentDetected()); } else { EXPECT_FALSE(mc.UpdateDetection(fake_stereo_frame)); - EXPECT_TRUE(mc.IsMultiChannelContentDetected()); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); } // Pass fake stereo frames and verify the behavior after any timeout. @@ -220,11 +229,129 @@ TEST_P(MultiChannelContentDetectorTimeoutBehavior, EXPECT_FALSE(mc.UpdateDetection(fake_stereo_frame)); if (detect_stereo_content && stereo_detection_timeout_threshold_frames > 0) { - EXPECT_FALSE(mc.IsMultiChannelContentDetected()); + EXPECT_FALSE(mc.IsProperMultiChannelContentDetected()); + } else { + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + } + } +} + +class MultiChannelContentDetectorHysteresisBehavior + : public ::testing::Test, + public ::testing::WithParamInterface<std::tuple<bool, float>> {}; + +INSTANTIATE_TEST_SUITE_P( + MultiChannelContentDetector, + MultiChannelContentDetectorHysteresisBehavior, + ::testing::Combine(::testing::Values(false, true), + ::testing::Values(0.0f, 0.1f, 0.2f))); + +TEST_P(MultiChannelContentDetectorHysteresisBehavior, + PeriodBeforeStereoDetectionIsTriggered) { + constexpr int kNumFramesPerSecond = 100; + const bool detect_stereo_content = std::get<0>(GetParam()); + const int stereo_detection_hysteresis_seconds = std::get<1>(GetParam()); + const int stereo_detection_hysteresis_frames = + stereo_detection_hysteresis_seconds * kNumFramesPerSecond; + + MultiChannelContentDetector mc( + detect_stereo_content, + /*num_render_input_channels=*/2, + /*detection_threshold=*/0.0f, + /*stereo_detection_timeout_threshold_seconds=*/0, + stereo_detection_hysteresis_seconds); + std::vector<std::vector<std::vector<float>>> true_stereo_frame = { + {std::vector<float>(160, 100.0f), std::vector<float>(160, 101.0f)}}; + + std::vector<std::vector<std::vector<float>>> fake_stereo_frame = { + {std::vector<float>(160, 100.0f), std::vector<float>(160, 100.0f)}}; + + // Pass fake stereo frames and verify the content detection. + for (int k = 0; k < 10; ++k) { + EXPECT_FALSE(mc.UpdateDetection(fake_stereo_frame)); + if (detect_stereo_content) { + EXPECT_FALSE(mc.IsProperMultiChannelContentDetected()); + } else { + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + } + EXPECT_FALSE(mc.IsTemporaryMultiChannelContentDetectedForTesting()); + } + + // Pass a two true stereo frames and verify that they are properly detected. + ASSERT_TRUE(stereo_detection_hysteresis_frames > 2 || + stereo_detection_hysteresis_frames == 0); + for (int k = 0; k < 2; ++k) { + if (detect_stereo_content) { + if (stereo_detection_hysteresis_seconds == 0.0f) { + if (k == 0) { + EXPECT_TRUE(mc.UpdateDetection(true_stereo_frame)); + } else { + EXPECT_FALSE(mc.UpdateDetection(true_stereo_frame)); + } + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + EXPECT_FALSE(mc.IsTemporaryMultiChannelContentDetectedForTesting()); + } else { + EXPECT_FALSE(mc.UpdateDetection(true_stereo_frame)); + EXPECT_FALSE(mc.IsProperMultiChannelContentDetected()); + EXPECT_TRUE(mc.IsTemporaryMultiChannelContentDetectedForTesting()); + } } else { - EXPECT_TRUE(mc.IsMultiChannelContentDetected()); + EXPECT_FALSE(mc.UpdateDetection(true_stereo_frame)); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + EXPECT_FALSE(mc.IsTemporaryMultiChannelContentDetectedForTesting()); } } + + if (stereo_detection_hysteresis_seconds == 0.0f) { + return; + } + + // Pass true stereo frames until any timeouts are about to occur. + for (int k = 0; k < stereo_detection_hysteresis_frames - 3; ++k) { + if (detect_stereo_content) { + EXPECT_FALSE(mc.UpdateDetection(true_stereo_frame)); + EXPECT_FALSE(mc.IsProperMultiChannelContentDetected()); + EXPECT_TRUE(mc.IsTemporaryMultiChannelContentDetectedForTesting()); + } else { + EXPECT_FALSE(mc.UpdateDetection(true_stereo_frame)); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + EXPECT_FALSE(mc.IsTemporaryMultiChannelContentDetectedForTesting()); + } + } + + // Pass a true stereo frame and verify that it is properly detected. + if (detect_stereo_content) { + EXPECT_TRUE(mc.UpdateDetection(true_stereo_frame)); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + EXPECT_FALSE(mc.IsTemporaryMultiChannelContentDetectedForTesting()); + } else { + EXPECT_FALSE(mc.UpdateDetection(true_stereo_frame)); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + EXPECT_FALSE(mc.IsTemporaryMultiChannelContentDetectedForTesting()); + } + + // Pass an additional true stereo frame and verify that it is properly + // detected. + if (detect_stereo_content) { + EXPECT_FALSE(mc.UpdateDetection(true_stereo_frame)); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + EXPECT_FALSE(mc.IsTemporaryMultiChannelContentDetectedForTesting()); + } else { + EXPECT_FALSE(mc.UpdateDetection(true_stereo_frame)); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + EXPECT_FALSE(mc.IsTemporaryMultiChannelContentDetectedForTesting()); + } + + // Pass a fake stereo frame and verify that it is properly detected. + if (detect_stereo_content) { + EXPECT_FALSE(mc.UpdateDetection(fake_stereo_frame)); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + EXPECT_FALSE(mc.IsTemporaryMultiChannelContentDetectedForTesting()); + } else { + EXPECT_FALSE(mc.UpdateDetection(fake_stereo_frame)); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + EXPECT_FALSE(mc.IsTemporaryMultiChannelContentDetectedForTesting()); + } } } // namespace webrtc |