Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include "WebRTC_DirectFilter.h"
- #include "Exception.h"
- #include "chronometer.h"
- WebRTC_DirectFilter::WebRTC_DirectFilter()
- {
- is_initialized = false;
- audioframe_counter = 0;
- statistics_display_counter = 0;
- component_enabled = false;
- io_embargo = false;
- disposed = false;
- disposing = false;
- EnableFilterDebugLog(true);
- }
- WebRTC_DirectFilter::~WebRTC_DirectFilter()
- {
- if (disposed || disposing) return;
- Dispose();
- }
- bool WebRTC_DirectFilter::Initialize(AudioChannels channel_config, bool enableDelayAgnoticism = false, bool enableSuperAEC = false)
- {
- Config config;
- if (enableDelayAgnoticism) {
- config.Set<DelayAgnostic>(new DelayAgnostic(true));
- Log(FLV_Framework::LOG_INFO, LOG << "Delay Agnostic activated.");
- }
- if (enableSuperAEC) {
- config.Set<ExtendedFilter>(new ExtendedFilter(true));
- Log(FLV_Framework::LOG_INFO, LOG << "Extended Filter activated.");
- }
- return this->Initialize(channel_config, config);
- }
- // If not initialized, the APM object will be created
- // But is he was already created, only the config will be applied
- // (Some configuration could not be applied when apm is already created,
- // call Dispose() then Initialize to force the reinstanciation)
- bool WebRTC_DirectFilter::Initialize(AudioChannels channel_config, Config& config)
- {
- try
- {
- ulock lock(apm_mutex);
- channels = channel_config;
- // Only if it's the first time, 1st step
- if(is_initialized == false) {
- apm.release();
- apm.reset(AudioProcessing::Create(config));
- }
- ResetClockDrift();
- apm->Initialize((int)channels.input.sample_rate, (int)channels.input.sample_rate, (int)channels.reverse.sample_rate,
- channels.input.channel_layout, channels.output.channel_layout, channels.reverse.channel_layout);
- apm->echo_cancellation()->enable_drift_compensation(true);
- apm->voice_detection()->Enable(true);
- apm->voice_detection()->set_likelihood(VoiceDetection::kModerateLikelihood);
- if(is_initialized == true) { // Only if it's already initialized
- apm->SetExtraOptions(config);
- }
- // Only if it's the first time, Last Step !
- // Needed to be called after the initialize
- if(is_initialized == false) {
- #if defined(_FILTER_DEBUG) && defined(_WIN32)
- reverse_file.Open("__reverse.wav", channels.reverse.sample_rate, channels.reverse.GetNumberOfChannels());
- process_file.Open("__process.wav", channels.input.sample_rate, channels.input.GetNumberOfChannels());
- out_file.Open("__out.wav", channels.output.sample_rate, channels.output.GetNumberOfChannels());
- out_nodebug_file.Open("__out_nodebug.wav", channels.output.sample_rate, channels.output.GetNumberOfChannels());
- apm->StartDebugRecording("DebugRecording", 0);
- #endif
- #ifdef __AECM_STORAGE
- ecp_storage.LoadFromStorage(false);
- #endif
- Log(LOG_VERBOSE, "Audio Filter initialized.");
- is_initialized = true; // we are correctly initialized
- }
- lock.unlock();
- }
- catch (std::exception &e) {
- Log(FLV_Framework::LOG_ERROR, LOG << "An exception occured when initializing Filter. (" << e.what() << ")");
- }
- catch (...) {
- Log(FLV_Framework::LOG_ERROR, LOG << "An unknow exception occured when initializing Filter.");
- }
- return is_initialized;
- }
- bool WebRTC_DirectFilter::Dispose() {
- if (disposing || disposed) return false;
- disposing = true;
- is_initialized = false;
- io_embargo = true;
- ulock lock(apm_mutex);
- #ifdef __AECM_STORAGE
- try {
- // saving first the echo_path if the is any to save
- if (apm->echo_control_mobile()->is_enabled()) {
- long size = apm->echo_control_mobile()->echo_path_size_bytes();
- std::shared_ptr<byte> data(new byte[size]);
- apm->echo_control_mobile()->GetEchoPath(data.get(), size);
- ecp_storage.Set(mode_echo_mobile, data, size);
- }
- }
- catch (std::exception& ex) {
- Log(FLV_Framework::LOG_ERROR, LOG << "Couldn't register echo path : " << ex.what());
- }
- #endif
- if (apm) {
- #ifdef _FILTER_DEBUG
- apm->StopDebugRecording();
- #endif
- delete apm.release(); // delete apm
- }
- lock.unlock();
- #if defined(_WIN32)
- reverse_file.Close();
- process_file.Close();
- out_file.Close();
- out_nodebug_file.Close();
- #endif
- //detection.Dispose();
- disposed = true;
- disposing = false;
- Log(FLV_Framework::LOG_VERBOSE, LOG << "Filter fully disposed.");
- return true;
- }
- // The delay expressed here is the delay between the speaker and the microphone in the library (and not the internet delay).
- void WebRTC_DirectFilter::SetDelay(uint16 delay)
- {
- if (IsReady() == false) return;
- ulock lock(apm_mutex);
- apm->set_stream_delay_ms(delay);
- }
- void WebRTC_DirectFilter::SetLogger(std::function<void(FLV_Framework::LogLevel, std::string)> loggerFunction)
- {
- LoggerClass::SetLogger(loggerFunction);
- #ifdef __AECM_STORAGE
- ecp_storage.SetLogger(loggerFunction);
- #endif
- }
- void WebRTC_DirectFilter::AecLog(FLV_Framework::LogLevel logLevel, std::string message)
- {
- if (enable_aec_log) Log(logLevel, message);
- }
- bool WebRTC_DirectFilter::EnableAEC(bool enable, bool useV3, bool useRefinedFilter) {
- if (IsReady() == false) return false;
- ulock lock(apm_mutex);
- if (enable && apm->echo_control_mobile()->is_enabled())
- EnableMobileAEC(false);
- #ifndef OSX // OSX don't support that (until we can use FLVFramework for 64 bits on osx)
- if(enable) // set aec v3 only if we activate aec
- {
- Config c;
- c.Set<EchoCanceller3>(new EchoCanceller3(useV3));
- c.Set<RefinedAdaptiveFilter>(new RefinedAdaptiveFilter(useRefinedFilter));
- apm->SetExtraOptions(c);
- }
- #endif
- apm->echo_cancellation()->Enable(enable);
- if (enable)
- {
- if (apm->echo_cancellation()->is_delay_logging_enabled() == false) {
- apm->echo_cancellation()->enable_metrics(true);
- apm->echo_cancellation()->enable_delay_logging(true);
- }
- Log(FLV_Framework::LOG_INFO, LOG << "Echo Cancellation activated.");
- }
- else
- {
- Log(FLV_Framework::LOG_INFO, LOG << "Echo Cancellation desactivated.");
- }
- lock.unlock();
- return true;
- }
- //TODO : Save an Echo path, and restore it
- bool WebRTC_DirectFilter::EnableMobileAEC(bool enable)
- {
- if (IsReady() == false) return false;
- if (enable && IsAEC())
- EnableAEC(false);
- ulock lock(apm_mutex);
- apm->echo_control_mobile()->Enable(enable);
- lock.unlock();
- if (enable)
- {
- Log(FLV_Framework::LOG_INFO, LOG << "Mobile Echo Cancellation activated.");
- }
- else
- {
- Log(FLV_Framework::LOG_INFO, LOG << "Mobile Echo Cancellation desactivated.");
- }
- SetDelay(0);
- return true;
- }
- bool WebRTC_DirectFilter::SetMobileAECMode(EnumEchoCancellationMobileMode mode)
- {
- if (IsReady() == false) return false;
- EchoControlMobile::RoutingMode mode_filter;
- switch (mode)
- {
- case QuietEarpieceOrHeadset:
- mode_filter = EchoControlMobile::kQuietEarpieceOrHeadset;
- break;
- case Earpiece:
- mode_filter = EchoControlMobile::kEarpiece;
- break;
- case LoudEarpiece:
- mode_filter = EchoControlMobile::kLoudEarpiece;
- break;
- case Speakerphone:
- mode_filter = EchoControlMobile::kSpeakerphone;
- break;
- case LoudSpeakerphone:
- mode_filter = EchoControlMobile::kLoudSpeakerphone;
- break;
- default:
- mode_filter = EchoControlMobile::kLoudEarpiece;
- break;
- }
- ulock lock(apm_mutex);
- mode_echo_mobile = mode;
- apm->echo_control_mobile()->set_routing_mode(mode_filter);
- #ifdef __AECM_STORAGE
- std::shared_ptr<WebRTC_EchoPathFile> ecp = ecp_storage.Get(mode);
- if (ecp != nullptr) {
- // If the size is different, we are not in the same situation, we erase the ecp file and bypass any SetEchoPath
- if (ecp->GetSize() != apm->echo_control_mobile()->echo_path_size_bytes()) {
- ecp_storage.Clear(mode);
- Log(FLV_Framework::LOG_VERBOSE, LOG << "Echo Path for " << static_cast<int>(mode) << " route mode is cleared.");
- } else {
- apm->echo_control_mobile()->SetEchoPath(ecp->GetData().get(), ecp->GetSize());
- Log(FLV_Framework::LOG_VERBOSE, LOG << "Echo Path for " << static_cast<int>(mode) << " route mode is set.");
- }
- }
- #endif
- lock.unlock();
- Log(FLV_Framework::LOG_INFO, LOG << "Mobile Echo Cancellation level set to " << mode_filter << ".");
- return true;
- }
- bool WebRTC_DirectFilter::SetAECLevel(EnumFilterLevel lvl) {
- if (IsReady() == false) return false;
- EchoCancellation::SuppressionLevel lvl_filter;
- switch (lvl)
- {
- case VeryHigh:
- case High:
- lvl_filter = EchoCancellation::kHighSuppression;
- break;
- case Moderate:
- lvl_filter = EchoCancellation::kModerateSuppression;
- break;
- case Low:
- lvl_filter = EchoCancellation::kLowSuppression;
- break;
- default:
- lvl_filter = EchoCancellation::kModerateSuppression;
- break;
- }
- ulock lock(apm_mutex);
- apm->echo_cancellation()->set_suppression_level(lvl_filter);
- lock.unlock();
- Log(FLV_Framework::LOG_INFO, LOG << "Echo Cancellation level set to " << lvl_filter << ".");
- return true;
- }
- bool WebRTC_DirectFilter::EnableNoiseSuppression(bool enable)
- {
- if (IsReady() == false) return false;
- ulock lock(apm_mutex);
- apm->noise_suppression()->Enable(enable);
- lock.unlock();
- if (enable)
- {
- Log(FLV_Framework::LOG_INFO, LOG << "Noise suppression activated.");
- }
- else
- {
- Log(FLV_Framework::LOG_INFO, LOG << "Noise suppression desactivated.");
- }
- return true;
- }
- bool WebRTC_DirectFilter::SetNoiseSuppressionLevel(EnumFilterLevel lvl)
- {
- if (IsReady() == false) return false;
- NoiseSuppression::Level lvlNoise;
- switch (lvl)
- {
- case VeryHigh:
- lvlNoise = NoiseSuppression::kVeryHigh;
- break;
- case High:
- lvlNoise = NoiseSuppression::kHigh;
- break;
- case Moderate:
- lvlNoise = NoiseSuppression::kModerate;
- break;
- case Low:
- lvlNoise = NoiseSuppression::kLow;
- break;
- default:
- lvlNoise = NoiseSuppression::kHigh;
- break;
- }
- ulock lock(apm_mutex);
- apm->noise_suppression()->set_level(lvlNoise);
- lock.unlock();
- Log(FLV_Framework::LOG_INFO, LOG << "Noise suppression level set to " << lvlNoise << ".");
- return true;
- }
- bool WebRTC_DirectFilter::EnableAutomaticGainControl(bool enable, bool withLimiter)
- {
- if (IsReady() == false) return false;
- ulock lock(apm_mutex);
- apm->gain_control()->Enable(enable);
- apm->gain_control()->enable_limiter(withLimiter);
- lock.unlock();
- if (enable)
- {
- Log(FLV_Framework::LOG_INFO, LOG << "Automatic gain control activated.");
- }
- else
- {
- Log(FLV_Framework::LOG_INFO, LOG << "Automatic gain control desactivated.");
- }
- return true;
- }
- bool WebRTC_DirectFilter::SetAGCLevel(int level, int compressionGain)
- {
- if (IsReady() == false) return false;
- ulock lock(apm_mutex);
- apm->gain_control()->set_target_level_dbfs(level);
- apm->gain_control()->set_compression_gain_db(compressionGain);
- lock.unlock();
- Log(FLV_Framework::LOG_INFO, LOG << "AGC level set to " << level << " dbfs and " << compressionGain << "db for compression gain.");
- return true;
- }
- bool WebRTC_DirectFilter::EnableHighPassFilter(bool enable)
- {
- if (IsReady() == false) return false;
- ulock lock(apm_mutex);
- apm->high_pass_filter()->Enable(enable);
- lock.unlock();
- if (enable)
- {
- Log(FLV_Framework::LOG_INFO, LOG << "High pass filter activated.");
- }
- else
- {
- Log(FLV_Framework::LOG_INFO, LOG << "High pass filter desactivated.");
- }
- return true;
- }
- // Don't work for Mobile AEC (AECM)
- bool WebRTC_DirectFilter::EnableDelayAgnosticism(bool enable)
- {
- if (IsReady() == false) return false;
- Config c;
- c.Set<DelayAgnostic>(new DelayAgnostic(enable));
- ulock lock(apm_mutex);
- apm->SetExtraOptions(c);
- lock.unlock();
- SetDelay(0);
- if (enable)
- {
- Log(FLV_Framework::LOG_INFO, LOG << "Delay agnosticism activated.");
- }
- else
- {
- Log(FLV_Framework::LOG_INFO, LOG << "Delay agnosticism desactivated.");
- }
- return true;
- }
- bool WebRTC_DirectFilter::EnableIntelligibilityEnhancement(bool enable)
- {
- if (IsReady() == false) return false;
- Config c;
- c.Set<Intelligibility>(new Intelligibility(enable));
- ulock lock(apm_mutex);
- apm->SetExtraOptions(c);
- lock.unlock();
- if (enable)
- {
- Log(FLV_Framework::LOG_INFO, LOG << "Intelligibility enhancement activated.");
- }
- else
- {
- Log(FLV_Framework::LOG_INFO, LOG << "Intelligibility enhancement desactivated.");
- }
- return true;
- }
- // Be careful, extend filter is more complex, it can add 25% of charge on the actual process.
- bool WebRTC_DirectFilter::EnableAECExtendedFilter(bool enable)
- {
- if (IsReady() == false) return false;
- Config c;
- c.Set<ExtendedFilter>(new ExtendedFilter(enable));
- ulock lock(apm_mutex);
- apm->SetExtraOptions(c);
- lock.unlock();
- if (enable)
- {
- Log(FLV_Framework::LOG_INFO, LOG << "Extend Filter activated.");
- }
- else
- {
- Log(FLV_Framework::LOG_INFO, LOG << "Extend Filter desactivated.");
- }
- return true;
- }
- bool WebRTC_DirectFilter::IsAEC() {
- if (IsReady() == false) return false;
- ulock lock(apm_mutex);
- return apm->echo_cancellation()->is_enabled();
- }
- void WebRTC_DirectFilter::ReverseStream(ShFLVDataFrame_Internal frame)
- {
- // if io_embargo, we ignore the frame
- if (IsReady() == false || io_embargo || component_enabled == false) return;
- try
- {
- ulock slock(apm_mutex);
- apm->ProcessReverseStream(frame->GetAudioFrame());
- slock.unlock();
- _playedSamples += frame->GetCount();
- last_speaker_delay = (int)frame->GetDelayCalculator().GetRenderDelay();
- }
- catch (std::exception& ex)
- {
- Log(FLV_Framework::LOG_ERROR, LOG << "An exception occured when enqueuing Analyze queue : " << ex.what());
- }
- catch (...)
- {
- Log(FLV_Framework::LOG_ERROR, LOG << "An unknow exception occured when enqueuing Analyze queue.");
- }
- }
- bool WebRTC_DirectFilter::ProcessStream(ShFLVDataFrame_Internal frame)
- {
- return false;
- }
- void WebRTC_DirectFilter::UpdateAudioConfiguration(const AudioChannels _channels)
- {
- if (is_initialized == false) return; // just update
- Config config;
- Initialize(_channels, config);
- }
- bool WebRTC_DirectFilter::ProcessStream(ShFLVDataFrame_Internal frame, ShFLVDataFrame_Internal& out_frame)
- {
- // if io_embargo, we ignore the frame
- if (IsReady() == false || io_embargo || component_enabled == false) return false; // is not ready we return
- bool ok_process = false;
- try
- {
- DelayCalculator d = frame->GetDelayCalculator();
- d.SetRenderDelay(last_speaker_delay);
- _recSamples += frame->GetCount();
- ulock slock(apm_mutex);
- int32_t drift = GetClockDrift(_playedSamples, _recSamples);
- apm->echo_cancellation()->set_stream_drift_samples(drift);
- apm->set_stream_delay_ms(static_cast<int>(d.GetDelay()));
- //ShFLVDataFrame_Internal out = resampler_process.Resample(frame);
- ShFLVDataFrame_Internal out(new FLVDataFrame_Internal((*frame.get()))); // cpy
- int err = apm->ProcessStream(out->GetAudioFrame());
- slock.unlock();
- if (err == 0) {
- out_frame = out;
- ok_process = true;
- }
- if (statistics_display_counter > 100) {
- DisplayDebugInfo(&d);
- statistics_display_counter = 0;
- }
- statistics_display_counter++;
- }
- catch (std::exception& ex)
- {
- Log(FLV_Framework::LOG_ERROR, LOG << "An exception occured when enqueuing Process queue : " << ex.what());
- ok_process = false;
- out_frame.reset();
- }
- catch (...)
- {
- Log(FLV_Framework::LOG_ERROR, LOG << "An unknow exception occured when enqueuing Process queue.");
- ok_process = false;
- out_frame.reset();
- }
- return ok_process;
- }
- int32_t WebRTC_DirectFilter::GetClockDrift(const uint32_t plSamp, const uint32_t rcSamp)
- {
- int drift = 0;
- unsigned int plSampDiff = 0, rcSampDiff = 0;
- if (plSamp >= _plSampOld)
- {
- plSampDiff = plSamp - _plSampOld;
- }
- else
- {
- // Wrap
- int i = 31;
- while (_plSampOld <= (unsigned int)std::pow(i, 2))
- {
- i--;
- }
- // Add the amount remaining prior to wrapping
- plSampDiff = plSamp + (unsigned int)std::pow(i + 1, 2) - _plSampOld;
- }
- if (rcSamp >= _rcSampOld)
- {
- rcSampDiff = rcSamp - _rcSampOld;
- }
- else
- { // Wrap
- int i = 31;
- while (_rcSampOld <= (unsigned int)std::pow(i, 2))
- {
- i--;
- }
- rcSampDiff = rcSamp + (unsigned int)std::pow(i + 1, 2) - _rcSampOld;
- }
- drift = plSampDiff - rcSampDiff;
- _plSampOld = plSamp;
- _rcSampOld = rcSamp;
- return drift;
- }
- void WebRTC_DirectFilter::DisplayDebugInfo(DelayCalculator* delay_calculator)
- {
- if (IsReady() == false) return;
- ulock lock(apm_mutex);
- try {
- EchoCancellation::Metrics echo_metrics;
- apm->echo_cancellation()->GetMetrics(&echo_metrics);
- int median = 0;
- int std = 0;
- float fraction_poor_delays = 0;
- apm->echo_cancellation()->GetDelayMetrics(&median, &std, &fraction_poor_delays);
- int drift = apm->echo_cancellation()->stream_drift_samples();
- int delay = 0;
- int delay_apm = apm->stream_delay_ms();
- if (delay_calculator != nullptr) {
- delay = (delay_calculator->GetDelay() == DELAY_CALCULATOR_ERROR ? -1 : static_cast<int>(delay_calculator->GetDelay()));
- }
- std::string ss = LOG << "[Filter] "
- << "Delay(d" << delay << ",apm" << delay_apm << ",drift" << drift << ") "
- << "HasEcho(" << apm->echo_cancellation()->stream_has_echo() << ") "
- << "HasVoice(" << apm->voice_detection()->stream_has_voice() << ") "
- << "MetricsINST(" << echo_metrics.a_nlp.instant << "," << echo_metrics.echo_return_loss.instant << "," << echo_metrics.echo_return_loss_enhancement.instant << "," << echo_metrics.residual_echo_return_loss.instant << ") "
- << "MetricsAVG(" << echo_metrics.a_nlp.average << "," << echo_metrics.echo_return_loss.average << "," << echo_metrics.echo_return_loss_enhancement.average << "," << echo_metrics.residual_echo_return_loss.average << ") "
- << "DelayMetrics(" << median << "," << std << "," << fraction_poor_delays << ")";
- AecLog(FLV_Framework::LOG_DEBUG, ss);
- }
- catch (std::exception& ex) {
- Log(FLV_Framework::LOG_ERROR, LOG << "An exception occured when logging large filter debug. " << ex.what());
- }
- catch (...) {
- Log(FLV_Framework::LOG_ERROR, "An unknow exception occured when logging large filter debug.");
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement