LCOV - code coverage report
Current view: top level - src/media/audio/audio-processing - webrtc.cpp (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 0.0 % 80 0
Test Date: 2026-11-09 10:41:14 Functions: 0.0 % 10 0

            Line data    Source code
       1              : /*
       2              :  *  Copyright (C) 2004-2026 Savoir-faire Linux Inc.
       3              :  *
       4              :  *  This program is free software: you can redistribute it and/or modify
       5              :  *  it under the terms of the GNU General Public License as published by
       6              :  *  the Free Software Foundation, either version 3 of the License, or
       7              :  *  (at your option) any later version.
       8              :  *
       9              :  *  This program is distributed in the hope that it will be useful,
      10              :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      11              :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      12              :  *  GNU General Public License for more details.
      13              :  *
      14              :  *  You should have received a copy of the GNU General Public License
      15              :  *  along with this program. If not, see <https://www.gnu.org/licenses/>.
      16              :  */
      17              : 
      18              : #include "webrtc.h"
      19              : #include "logger.h"
      20              : 
      21              : #if __has_include(<common_audio/vad/include/webrtc_vad.h>)
      22              : #include <common_audio/vad/include/webrtc_vad.h>
      23              : #define HAVE_WEBRTC_VAD 1
      24              : #else
      25              : // Only installed by the Jami contrib build (see contrib/src/webrtc-audio-processing)
      26              : #define HAVE_WEBRTC_VAD 0
      27              : #endif
      28              : 
      29              : #include <algorithm>
      30              : #include <cmath>
      31              : 
      32              : namespace jami {
      33              : 
      34              : inline size_t
      35            0 : webrtcFrameSize(AudioFormat format)
      36              : {
      37            0 :     return (size_t) (webrtc::AudioProcessing::kChunkSizeMs * format.sample_rate / 1000);
      38              : }
      39              : 
      40              : constexpr int webrtcNoError = webrtc::AudioProcessing::kNoError;
      41              : 
      42              : void
      43            0 : WebRTCAudioProcessor::VadDeleter::operator()(VadInst* vad) const
      44              : {
      45              : #if HAVE_WEBRTC_VAD
      46            0 :     WebRtcVad_Free(vad);
      47              : #else
      48              :     (void) vad;
      49              : #endif
      50            0 : }
      51              : 
      52            0 : WebRTCAudioProcessor::WebRTCAudioProcessor(AudioFormat format, unsigned /* frameSize */)
      53            0 :     : AudioProcessor(format.withSampleFormat(AV_SAMPLE_FMT_FLTP), webrtcFrameSize(format))
      54              : {
      55            0 :     JAMI_LOG("[webrtc-ap] WebRTCAudioProcessor, frame size = {:d} (={:d} ms), channels = {:d}",
      56              :              frameSize_,
      57              :              frameDurationMs_,
      58              :              format_.nb_channels);
      59              : 
      60            0 :     apm = webrtc::AudioProcessingBuilder().Create();
      61              : 
      62            0 :     webrtc::StreamConfig streamConfig((int) format_.sample_rate, (int) format_.nb_channels);
      63              :     webrtc::ProcessingConfig pconfig = {
      64              :         streamConfig, /* input stream */
      65              :         streamConfig, /* output stream */
      66              :         streamConfig, /* reverse input stream */
      67              :         streamConfig, /* reverse output stream */
      68            0 :     };
      69              : 
      70            0 :     if (apm->Initialize(pconfig) != webrtcNoError) {
      71            0 :         JAMI_ERROR("[webrtc-ap] Error initialising audio processing module");
      72              :     }
      73              : 
      74              :     // The APM dropped its voice detector in 1.0; use the standalone WebRTC VAD
      75              :     // (what the old VoiceDetection component wrapped) on the first channel.
      76              : #if HAVE_WEBRTC_VAD
      77            0 :     if (WebRtcVad_ValidRateAndFrameLength((int) format_.sample_rate, frameSize_) == 0) {
      78            0 :         vad_.reset(WebRtcVad_Create());
      79            0 :         if (WebRtcVad_Init(vad_.get()) != 0) {
      80            0 :             JAMI_ERROR("[webrtc-ap] Error initialising voice activity detection");
      81            0 :             vad_.reset();
      82              :         }
      83            0 :         vadBuffer_.resize(frameSize_);
      84              :     } else {
      85            0 :         JAMI_WARNING("[webrtc-ap] {:d} Hz unsupported by the WebRTC VAD, voice activity disabled", format_.sample_rate);
      86              :     }
      87              : #else
      88              :     JAMI_WARNING("[webrtc-ap] Built without the WebRTC VAD, voice activity disabled");
      89              : #endif
      90            0 : }
      91              : 
      92              : void
      93            0 : WebRTCAudioProcessor::applyConfig()
      94              : {
      95            0 :     apm->ApplyConfig(config_);
      96            0 : }
      97              : 
      98              : void
      99            0 : WebRTCAudioProcessor::enableNoiseSuppression(bool enabled)
     100              : {
     101            0 :     JAMI_LOG("[webrtc-ap] enableNoiseSuppression {}", enabled);
     102            0 :     config_.noise_suppression.enabled = enabled;
     103            0 :     config_.noise_suppression.level = webrtc::AudioProcessing::Config::NoiseSuppression::kVeryHigh;
     104            0 :     config_.high_pass_filter.enabled = enabled;
     105            0 :     applyConfig();
     106            0 : }
     107              : 
     108              : void
     109            0 : WebRTCAudioProcessor::enableAutomaticGainControl(bool enabled)
     110              : {
     111            0 :     JAMI_LOG("[webrtc-ap] enableAutomaticGainControl {}", enabled);
     112            0 :     config_.gain_controller1.enabled = enabled;
     113            0 :     config_.gain_controller1.mode = webrtc::AudioProcessing::Config::GainController1::kAdaptiveAnalog;
     114            0 :     applyConfig();
     115            0 : }
     116              : 
     117              : void
     118            0 : WebRTCAudioProcessor::enableEchoCancel(bool enabled)
     119              : {
     120            0 :     JAMI_LOG("[webrtc-ap] enableEchoCancel {}", enabled);
     121            0 :     config_.echo_canceller.enabled = enabled;
     122            0 :     config_.echo_canceller.mobile_mode = false;
     123            0 :     applyConfig();
     124            0 : }
     125              : 
     126              : void
     127            0 : WebRTCAudioProcessor::enableVoiceActivityDetection(bool enabled)
     128              : {
     129            0 :     JAMI_LOG("[webrtc-ap] enableVoiceActivityDetection {}", enabled);
     130            0 :     if (!vad_) {
     131            0 :         if (enabled)
     132            0 :             JAMI_ERROR("[webrtc-ap] Voice activity detection unavailable");
     133            0 :         return;
     134              :     }
     135              : #if HAVE_WEBRTC_VAD
     136              :     // Mode 3 is what VoiceDetection::kVeryLowLikelihood mapped to.
     137            0 :     if (WebRtcVad_set_mode(vad_.get(), enabled ? 3 : 0) != 0) {
     138            0 :         JAMI_ERROR("[webrtc-ap] Error setting voice detection mode");
     139              :     }
     140              : #endif
     141            0 :     detectVoice_ = enabled;
     142              : }
     143              : 
     144              : std::shared_ptr<AudioFrame>
     145            0 : WebRTCAudioProcessor::getProcessed()
     146              : {
     147            0 :     if (tidyQueues()) {
     148            0 :         return {};
     149              :     }
     150              : 
     151            0 :     auto playback = playbackQueue_.dequeue();
     152            0 :     auto record = recordQueue_.dequeue();
     153            0 :     if (!playback || !record) {
     154            0 :         return {};
     155              :     }
     156            0 :     webrtc::StreamConfig sc((int) format_.sample_rate, (int) format_.nb_channels);
     157              : 
     158              :     // process reverse in place
     159            0 :     float** playData = (float**) playback->pointer()->extended_data;
     160            0 :     if (apm->ProcessReverseStream(playData, sc, sc, playData) != webrtcNoError) {
     161            0 :         JAMI_ERROR("[webrtc-ap] ProcessReverseStream failed");
     162              :     }
     163              : 
     164              :     // AEC3 estimates the delay itself; 0 keeps the API contract satisfied.
     165            0 :     if (apm->set_stream_delay_ms(0) != webrtcNoError) {
     166            0 :         JAMI_ERROR("[webrtc-ap] set_stream_delay_ms failed");
     167              :     }
     168            0 :     if (config_.gain_controller1.enabled) {
     169            0 :         apm->set_stream_analog_level(analogLevel_);
     170              :     }
     171              : 
     172              :     // process deinterleaved float recorded data in place
     173            0 :     float** recData = (float**) record->pointer()->extended_data;
     174            0 :     if (apm->ProcessStream(recData, sc, sc, recData) != webrtcNoError) {
     175            0 :         JAMI_ERROR("[webrtc-ap] ProcessStream failed");
     176              :     }
     177              : 
     178            0 :     if (config_.gain_controller1.enabled) {
     179            0 :         analogLevel_ = apm->recommended_stream_analog_level();
     180              :     }
     181              : 
     182            0 :     bool hasVoice = false;
     183              : #if HAVE_WEBRTC_VAD
     184            0 :     if (detectVoice_ && vad_) {
     185            0 :         std::transform(recData[0], recData[0] + frameSize_, vadBuffer_.begin(), [](float s) {
     186            0 :             return (int16_t) std::lround(std::clamp(s, -1.f, 1.f) * 32767.f);
     187              :         });
     188            0 :         hasVoice = WebRtcVad_Process(vad_.get(), (int) format_.sample_rate, vadBuffer_.data(), vadBuffer_.size()) == 1;
     189              :     }
     190              : #endif
     191            0 :     record->has_voice = detectVoice_ && getStabilizedVoiceActivity(hasVoice);
     192            0 :     return record;
     193            0 : }
     194              : 
     195              : } // namespace jami
        

Generated by: LCOV version 2.0-1