Adrien Beraud | 087d559 | 2023-03-06 11:22:33 -0500 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2004-2023 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 "resampler.h" |
| 24 | |
| 25 | extern "C" { |
| 26 | #include <libswresample/swresample.h> |
| 27 | #include <libavutil/opt.h> |
| 28 | } |
| 29 | |
| 30 | #include <new> |
| 31 | #include <stdexcept> |
| 32 | #include <iostream> |
| 33 | |
| 34 | namespace jami { |
| 35 | |
| 36 | Resampler::Resampler() |
| 37 | : swrCtx_(swr_alloc()) |
| 38 | , initCount_(0) |
| 39 | {} |
| 40 | |
| 41 | Resampler::~Resampler() |
| 42 | { |
| 43 | swr_free(&swrCtx_); |
| 44 | } |
| 45 | |
| 46 | void |
| 47 | Resampler::reinit(const AVFrame* in, const AVFrame* out) |
| 48 | { |
| 49 | // NOTE swr_set_matrix should be called on an uninitialized context |
| 50 | auto swrCtx = swr_alloc(); |
| 51 | if (!swrCtx) { |
| 52 | throw std::bad_alloc(); |
| 53 | } |
| 54 | |
| 55 | av_opt_set_chlayout(swrCtx, "ichl", &in->ch_layout, 0); |
| 56 | av_opt_set_int(swrCtx, "isr", in->sample_rate, 0); |
| 57 | av_opt_set_sample_fmt(swrCtx, "isf", static_cast<AVSampleFormat>(in->format), 0); |
| 58 | |
| 59 | av_opt_set_chlayout(swrCtx, "ochl", &out->ch_layout, 0); |
| 60 | av_opt_set_int(swrCtx, "osr", out->sample_rate, 0); |
| 61 | av_opt_set_sample_fmt(swrCtx, "osf", static_cast<AVSampleFormat>(out->format), 0); |
| 62 | |
| 63 | /** |
| 64 | * Downmixing from 5.1 requires extra setup, since libswresample can't do it automatically |
| 65 | * (not yet implemented). |
| 66 | * |
| 67 | * Source: https://www.atsc.org/wp-content/uploads/2015/03/A52-201212-17.pdf |
| 68 | * Section 7.8.2 for the algorithm |
| 69 | * Tables 5.9 and 5.10 for the coefficients clev and slev |
| 70 | * |
| 71 | * LFE downmixing is optional, so any coefficient can be used, we use +6dB for mono and |
| 72 | * +0dB in each channel for stereo. |
| 73 | */ |
| 74 | if (in->ch_layout.u.mask == AV_CH_LAYOUT_5POINT1 |
| 75 | || in->ch_layout.u.mask == AV_CH_LAYOUT_5POINT1_BACK) { |
| 76 | // NOTE MSVC can't allocate dynamic size arrays on the stack |
| 77 | if (out->ch_layout.nb_channels == 2) { |
| 78 | double matrix[2][6]; |
| 79 | // L = 1.0*FL + 0.707*FC + 0.707*BL + 1.0*LFE |
| 80 | matrix[0][0] = 1; |
| 81 | matrix[0][1] = 0; |
| 82 | matrix[0][2] = 0.707; |
| 83 | matrix[0][3] = 1; |
| 84 | matrix[0][4] = 0.707; |
| 85 | matrix[0][5] = 0; |
| 86 | // R = 1.0*FR + 0.707*FC + 0.707*BR + 1.0*LFE |
| 87 | matrix[1][0] = 0; |
| 88 | matrix[1][1] = 1; |
| 89 | matrix[1][2] = 0.707; |
| 90 | matrix[1][3] = 1; |
| 91 | matrix[1][4] = 0; |
| 92 | matrix[1][5] = 0.707; |
| 93 | swr_set_matrix(swrCtx, matrix[0], 6); |
| 94 | } else { |
| 95 | double matrix[1][6]; |
| 96 | // M = 1.0*FL + 1.414*FC + 1.0*FR + 0.707*BL + 0.707*BR + 2.0*LFE |
| 97 | matrix[0][0] = 1; |
| 98 | matrix[0][1] = 1; |
| 99 | matrix[0][2] = 1.414; |
| 100 | matrix[0][3] = 2; |
| 101 | matrix[0][4] = 0.707; |
| 102 | matrix[0][5] = 0.707; |
| 103 | swr_set_matrix(swrCtx, matrix[0], 6); |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | if (swr_init(swrCtx) >= 0) { |
| 108 | std::swap(swrCtx_, swrCtx); |
| 109 | swr_free(&swrCtx); |
| 110 | ++initCount_; |
| 111 | } else { |
| 112 | throw std::runtime_error("Failed to initialize resampler context"); |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | int |
| 117 | Resampler::resample(const AVFrame* input, AVFrame* output) |
| 118 | { |
| 119 | if (!initCount_) |
| 120 | reinit(input, output); |
| 121 | |
| 122 | int ret = swr_convert_frame(swrCtx_, output, input); |
| 123 | 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 | if (initCount_ > 1) { |
| 128 | throw std::runtime_error("Infinite loop detected in audio resampler"); |
| 129 | } |
| 130 | reinit(input, output); |
| 131 | return resample(input, output); |
| 132 | } else if (ret < 0) { |
| 133 | return -1; |
| 134 | } |
| 135 | |
| 136 | // Resampling worked, reset count to 1 so reinit isn't called again |
| 137 | initCount_ = 1; |
| 138 | return 0; |
| 139 | } |
| 140 | } // namespace jami |