LCOV - code coverage report
Current view: top level - src/media/audio - resampler.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 7 92 7.6 %
Date: 2024-04-19 19:18:04 Functions: 2 10 20.0 %

          Line data    Source code
       1             : /*
       2             :  *  Copyright (C) 2004-2024 Savoir-faire Linux Inc.
       3             :  *
       4             :  *  Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
       5             :  *  Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
       6             :  *  Author: Philippe Gorley <philippe.gorley@savoirfairelinux.com>
       7             :  *
       8             :  *  This program is free software; you can redistribute it and/or modify
       9             :  *  it under the terms of the GNU General Public License as published by
      10             :  *  the Free Software Foundation; either version 3 of the License, or
      11             :  *  (at your option) any later version.
      12             :  *
      13             :  *  This program is distributed in the hope that it will be useful,
      14             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             :  *  GNU General Public License for more details.
      17             :  *
      18             :  *  You should have received a copy of the GNU General Public License
      19             :  *  along with this program; if not, write to the Free Software
      20             :  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
      21             :  */
      22             : 
      23             : #include "libav_deps.h"
      24             : #include "logger.h"
      25             : #include "resampler.h"
      26             : 
      27             : extern "C" {
      28             : #include <libswresample/swresample.h>
      29             : }
      30             : 
      31             : namespace jami {
      32             : 
      33         785 : Resampler::Resampler()
      34         785 :     : swrCtx_(swr_alloc())
      35         785 :     , initCount_(0)
      36         785 : {}
      37             : 
      38         785 : Resampler::~Resampler()
      39             : {
      40         785 :     swr_free(&swrCtx_);
      41         785 : }
      42             : 
      43             : void
      44           0 : Resampler::reinit(const AVFrame* in, const AVFrame* out)
      45             : {
      46             :     // NOTE swr_set_matrix should be called on an uninitialized context
      47           0 :     auto swrCtx = swr_alloc();
      48           0 :     if (!swrCtx) {
      49           0 :         JAMI_ERR() << "Cannot allocate resampler context";
      50           0 :         throw std::bad_alloc();
      51             :     }
      52             : 
      53           0 :     av_opt_set_chlayout(swrCtx, "ichl", &in->ch_layout, 0);
      54           0 :     av_opt_set_int(swrCtx, "isr", in->sample_rate, 0);
      55           0 :     av_opt_set_sample_fmt(swrCtx, "isf", static_cast<AVSampleFormat>(in->format), 0);
      56             : 
      57           0 :     av_opt_set_chlayout(swrCtx, "ochl", &out->ch_layout, 0);
      58           0 :     av_opt_set_int(swrCtx, "osr", out->sample_rate, 0);
      59           0 :     av_opt_set_sample_fmt(swrCtx, "osf", static_cast<AVSampleFormat>(out->format), 0);
      60             : 
      61             :     /**
      62             :      * Downmixing from 5.1 requires extra setup, since libswresample can't do it automatically
      63             :      * (not yet implemented).
      64             :      *
      65             :      * Source: https://www.atsc.org/wp-content/uploads/2015/03/A52-201212-17.pdf
      66             :      * Section 7.8.2 for the algorithm
      67             :      * Tables 5.9 and 5.10 for the coefficients clev and slev
      68             :      *
      69             :      * LFE downmixing is optional, so any coefficient can be used, we use +6dB for mono and
      70             :      * +0dB in each channel for stereo.
      71             :      */
      72           0 :     if (in->ch_layout.u.mask == AV_CH_LAYOUT_5POINT1
      73           0 :         || in->ch_layout.u.mask == AV_CH_LAYOUT_5POINT1_BACK) {
      74             :         // NOTE MSVC can't allocate dynamic size arrays on the stack
      75           0 :         if (out->ch_layout.nb_channels == 2) {
      76             :             double matrix[2][6];
      77             :             // L = 1.0*FL + 0.707*FC + 0.707*BL + 1.0*LFE
      78           0 :             matrix[0][0] = 1;
      79           0 :             matrix[0][1] = 0;
      80           0 :             matrix[0][2] = 0.707;
      81           0 :             matrix[0][3] = 1;
      82           0 :             matrix[0][4] = 0.707;
      83           0 :             matrix[0][5] = 0;
      84             :             // R = 1.0*FR + 0.707*FC + 0.707*BR + 1.0*LFE
      85           0 :             matrix[1][0] = 0;
      86           0 :             matrix[1][1] = 1;
      87           0 :             matrix[1][2] = 0.707;
      88           0 :             matrix[1][3] = 1;
      89           0 :             matrix[1][4] = 0;
      90           0 :             matrix[1][5] = 0.707;
      91           0 :             swr_set_matrix(swrCtx, matrix[0], 6);
      92             :         } else {
      93             :             double matrix[1][6];
      94             :             // M = 1.0*FL + 1.414*FC + 1.0*FR + 0.707*BL + 0.707*BR + 2.0*LFE
      95           0 :             matrix[0][0] = 1;
      96           0 :             matrix[0][1] = 1;
      97           0 :             matrix[0][2] = 1.414;
      98           0 :             matrix[0][3] = 2;
      99           0 :             matrix[0][4] = 0.707;
     100           0 :             matrix[0][5] = 0.707;
     101           0 :             swr_set_matrix(swrCtx, matrix[0], 6);
     102             :         }
     103             :     }
     104             : 
     105           0 :     if (swr_init(swrCtx) >= 0) {
     106           0 :         std::swap(swrCtx_, swrCtx);
     107           0 :         swr_free(&swrCtx);
     108           0 :         ++initCount_;
     109             :     } else {
     110           0 :         std::string msg = "Failed to initialize resampler context";
     111           0 :         JAMI_ERR() << msg;
     112           0 :         throw std::runtime_error(msg);
     113           0 :     }
     114           0 : }
     115             : 
     116             : int
     117           0 : Resampler::resample(const AVFrame* input, AVFrame* output)
     118             : {
     119           0 :     if (!initCount_)
     120           0 :         reinit(input, output);
     121             : 
     122           0 :     int ret = swr_convert_frame(swrCtx_, output, input);
     123           0 :     if (ret & AVERROR_INPUT_CHANGED || ret & AVERROR_OUTPUT_CHANGED) {
     124             :         // Under certain conditions, the resampler reinits itself in an infinite loop. This is
     125             :         // indicative of an underlying problem in the code. This check is so the backtrace
     126             :         // doesn't get mangled with a bunch of calls to Resampler::resample
     127           0 :         if (initCount_ > 1) {
     128           0 :             JAMI_ERROR("Infinite loop detected in audio resampler, please open an issue on https://git.jami.net");
     129           0 :             throw std::runtime_error("Resampler");
     130             :         }
     131           0 :         reinit(input, output);
     132           0 :         return resample(input, output);
     133           0 :     } else if (ret < 0) {
     134           0 :         JAMI_ERROR("Failed to resample frame");
     135           0 :         return -1;
     136             :     }
     137             : 
     138             :     // Resampling worked, reset count to 1 so reinit isn't called again
     139           0 :     initCount_ = 1;
     140           0 :     return 0;
     141             : }
     142             : 
     143             : std::unique_ptr<AudioFrame>
     144           0 : Resampler::resample(std::unique_ptr<AudioFrame>&& in, const AudioFormat& format)
     145             : {
     146           0 :     if (in->pointer()->sample_rate == (int) format.sample_rate
     147           0 :         && in->pointer()->ch_layout.nb_channels == (int) format.nb_channels
     148           0 :         && (AVSampleFormat) in->pointer()->format == format.sampleFormat) {
     149           0 :         return std::move(in);
     150             :     }
     151           0 :     auto output = std::make_unique<AudioFrame>(format);
     152           0 :     resample(in->pointer(), output->pointer());
     153           0 :     output->has_voice = in->has_voice;
     154           0 :     return output;
     155           0 : }
     156             : 
     157             : std::shared_ptr<AudioFrame>
     158           0 : Resampler::resample(std::shared_ptr<AudioFrame>&& in, const AudioFormat& format)
     159             : {
     160           0 :     if (not in) {
     161           0 :         return {};
     162             :     }
     163           0 :     auto inPtr = in->pointer();
     164           0 :     if (inPtr == nullptr) {
     165           0 :         return {};
     166             :     }
     167             : 
     168           0 :     if (inPtr->sample_rate == (int) format.sample_rate
     169           0 :         && inPtr->ch_layout.nb_channels == (int) format.nb_channels
     170           0 :         && (AVSampleFormat) inPtr->format == format.sampleFormat) {
     171           0 :         return std::move(in);
     172             :     }
     173             : 
     174           0 :     auto output = std::make_shared<AudioFrame>(format);
     175           0 :     if (auto outPtr = output->pointer()) {
     176           0 :         resample(inPtr, outPtr);
     177           0 :         output->has_voice = in->has_voice;
     178           0 :         return output;
     179             :     }
     180           0 :     return {};
     181           0 : }
     182             : 
     183             : } // namespace jami

Generated by: LCOV version 1.14