blob: 9d2a06d44e8784dfb6fba341d932734c05aa91b0 [file] [log] [blame]
/*
* Copyright (C) 2004-2023 Savoir-faire Linux Inc.
*
* Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
* Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
* Author: Philippe Gorley <philippe.gorley@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "resampler.h"
extern "C" {
#include <libswresample/swresample.h>
#include <libavutil/opt.h>
}
#include <new>
#include <stdexcept>
#include <iostream>
namespace jami {
Resampler::Resampler()
: swrCtx_(swr_alloc())
, initCount_(0)
{}
Resampler::~Resampler()
{
swr_free(&swrCtx_);
}
void
Resampler::reinit(const AVFrame* in, const AVFrame* out)
{
// NOTE swr_set_matrix should be called on an uninitialized context
auto swrCtx = swr_alloc();
if (!swrCtx) {
throw std::bad_alloc();
}
av_opt_set_chlayout(swrCtx, "ichl", &in->ch_layout, 0);
av_opt_set_int(swrCtx, "isr", in->sample_rate, 0);
av_opt_set_sample_fmt(swrCtx, "isf", static_cast<AVSampleFormat>(in->format), 0);
av_opt_set_chlayout(swrCtx, "ochl", &out->ch_layout, 0);
av_opt_set_int(swrCtx, "osr", out->sample_rate, 0);
av_opt_set_sample_fmt(swrCtx, "osf", static_cast<AVSampleFormat>(out->format), 0);
/**
* Downmixing from 5.1 requires extra setup, since libswresample can't do it automatically
* (not yet implemented).
*
* Source: https://www.atsc.org/wp-content/uploads/2015/03/A52-201212-17.pdf
* Section 7.8.2 for the algorithm
* Tables 5.9 and 5.10 for the coefficients clev and slev
*
* LFE downmixing is optional, so any coefficient can be used, we use +6dB for mono and
* +0dB in each channel for stereo.
*/
if (in->ch_layout.u.mask == AV_CH_LAYOUT_5POINT1
|| in->ch_layout.u.mask == AV_CH_LAYOUT_5POINT1_BACK) {
// NOTE MSVC can't allocate dynamic size arrays on the stack
if (out->ch_layout.nb_channels == 2) {
double matrix[2][6];
// L = 1.0*FL + 0.707*FC + 0.707*BL + 1.0*LFE
matrix[0][0] = 1;
matrix[0][1] = 0;
matrix[0][2] = 0.707;
matrix[0][3] = 1;
matrix[0][4] = 0.707;
matrix[0][5] = 0;
// R = 1.0*FR + 0.707*FC + 0.707*BR + 1.0*LFE
matrix[1][0] = 0;
matrix[1][1] = 1;
matrix[1][2] = 0.707;
matrix[1][3] = 1;
matrix[1][4] = 0;
matrix[1][5] = 0.707;
swr_set_matrix(swrCtx, matrix[0], 6);
} else {
double matrix[1][6];
// M = 1.0*FL + 1.414*FC + 1.0*FR + 0.707*BL + 0.707*BR + 2.0*LFE
matrix[0][0] = 1;
matrix[0][1] = 1;
matrix[0][2] = 1.414;
matrix[0][3] = 2;
matrix[0][4] = 0.707;
matrix[0][5] = 0.707;
swr_set_matrix(swrCtx, matrix[0], 6);
}
}
if (swr_init(swrCtx) >= 0) {
std::swap(swrCtx_, swrCtx);
swr_free(&swrCtx);
++initCount_;
} else {
throw std::runtime_error("Failed to initialize resampler context");
}
}
int
Resampler::resample(const AVFrame* input, AVFrame* output)
{
if (!initCount_)
reinit(input, output);
int ret = swr_convert_frame(swrCtx_, output, input);
if (ret & AVERROR_INPUT_CHANGED || ret & AVERROR_OUTPUT_CHANGED) {
// Under certain conditions, the resampler reinits itself in an infinite loop. This is
// indicative of an underlying problem in the code. This check is so the backtrace
// doesn't get mangled with a bunch of calls to Resampler::resample
if (initCount_ > 1) {
throw std::runtime_error("Infinite loop detected in audio resampler");
}
reinit(input, output);
return resample(input, output);
} else if (ret < 0) {
return -1;
}
// Resampling worked, reset count to 1 so reinit isn't called again
initCount_ = 1;
return 0;
}
} // namespace jami