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
|