Line data Source code
1 : /*
2 : * Copyright (C) 2004-2024 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 "libav_deps.h"
19 : #include "logger.h"
20 : #include "resampler.h"
21 :
22 : extern "C" {
23 : #include <libswresample/swresample.h>
24 : }
25 :
26 : namespace jami {
27 :
28 866 : Resampler::Resampler()
29 866 : : swrCtx_(swr_alloc())
30 866 : , initCount_(0)
31 866 : {}
32 :
33 866 : Resampler::~Resampler()
34 : {
35 866 : swr_free(&swrCtx_);
36 866 : }
37 :
38 : void
39 0 : Resampler::reinit(const AVFrame* in, const AVFrame* out)
40 : {
41 : // NOTE swr_set_matrix should be called on an uninitialized context
42 0 : auto swrCtx = swr_alloc();
43 0 : if (!swrCtx) {
44 0 : JAMI_ERR() << "Unable to allocate resampler context";
45 0 : throw std::bad_alloc();
46 : }
47 :
48 0 : av_opt_set_chlayout(swrCtx, "ichl", &in->ch_layout, 0);
49 0 : av_opt_set_int(swrCtx, "isr", in->sample_rate, 0);
50 0 : av_opt_set_sample_fmt(swrCtx, "isf", static_cast<AVSampleFormat>(in->format), 0);
51 :
52 0 : av_opt_set_chlayout(swrCtx, "ochl", &out->ch_layout, 0);
53 0 : av_opt_set_int(swrCtx, "osr", out->sample_rate, 0);
54 0 : av_opt_set_sample_fmt(swrCtx, "osf", static_cast<AVSampleFormat>(out->format), 0);
55 :
56 : /**
57 : * Downmixing from 5.1 requires extra setup, since libswresample is unable to do it
58 : * automatically (not yet implemented).
59 : *
60 : * Source: https://www.atsc.org/wp-content/uploads/2015/03/A52-201212-17.pdf
61 : * Section 7.8.2 for the algorithm
62 : * Tables 5.9 and 5.10 for the coefficients clev and slev
63 : *
64 : * LFE downmixing is optional, so any coefficient can be used, we use +6dB for mono and
65 : * +0dB in each channel for stereo.
66 : */
67 0 : if (in->ch_layout.u.mask == AV_CH_LAYOUT_5POINT1
68 0 : || in->ch_layout.u.mask == AV_CH_LAYOUT_5POINT1_BACK) {
69 : // NOTE: MSVC is unable to allocate dynamic size arrays on the stack
70 0 : if (out->ch_layout.nb_channels == 2) {
71 : double matrix[2][6];
72 : // L = 1.0*FL + 0.707*FC + 0.707*BL + 1.0*LFE
73 0 : matrix[0][0] = 1;
74 0 : matrix[0][1] = 0;
75 0 : matrix[0][2] = 0.707;
76 0 : matrix[0][3] = 1;
77 0 : matrix[0][4] = 0.707;
78 0 : matrix[0][5] = 0;
79 : // R = 1.0*FR + 0.707*FC + 0.707*BR + 1.0*LFE
80 0 : matrix[1][0] = 0;
81 0 : matrix[1][1] = 1;
82 0 : matrix[1][2] = 0.707;
83 0 : matrix[1][3] = 1;
84 0 : matrix[1][4] = 0;
85 0 : matrix[1][5] = 0.707;
86 0 : swr_set_matrix(swrCtx, matrix[0], 6);
87 : } else {
88 : double matrix[1][6];
89 : // M = 1.0*FL + 1.414*FC + 1.0*FR + 0.707*BL + 0.707*BR + 2.0*LFE
90 0 : matrix[0][0] = 1;
91 0 : matrix[0][1] = 1;
92 0 : matrix[0][2] = 1.414;
93 0 : matrix[0][3] = 2;
94 0 : matrix[0][4] = 0.707;
95 0 : matrix[0][5] = 0.707;
96 0 : swr_set_matrix(swrCtx, matrix[0], 6);
97 : }
98 : }
99 :
100 0 : if (swr_init(swrCtx) >= 0) {
101 0 : std::swap(swrCtx_, swrCtx);
102 0 : swr_free(&swrCtx);
103 0 : ++initCount_;
104 : } else {
105 0 : std::string msg = "Failed to initialize resampler context";
106 0 : JAMI_ERR() << msg;
107 0 : throw std::runtime_error(msg);
108 0 : }
109 0 : }
110 :
111 : int
112 0 : Resampler::resample(const AVFrame* input, AVFrame* output)
113 : {
114 0 : if (!initCount_)
115 0 : reinit(input, output);
116 :
117 0 : int ret = swr_convert_frame(swrCtx_, output, input);
118 0 : if (ret & AVERROR_INPUT_CHANGED || ret & AVERROR_OUTPUT_CHANGED) {
119 : // Under certain conditions, the resampler reinits itself in an infinite loop. This is
120 : // indicative of an underlying problem in the code. This check is so the backtrace
121 : // doesn't get mangled with a bunch of calls to Resampler::resample
122 0 : if (initCount_ > 1) {
123 0 : JAMI_ERROR("Infinite loop detected in audio resampler, please open an issue on https://git.jami.net");
124 0 : throw std::runtime_error("Resampler");
125 : }
126 0 : reinit(input, output);
127 0 : return resample(input, output);
128 0 : } else if (ret < 0) {
129 0 : JAMI_ERROR("Failed to resample frame");
130 0 : return -1;
131 : }
132 :
133 : // Resampling worked, reset count to 1 so reinit isn't called again
134 0 : initCount_ = 1;
135 0 : return 0;
136 : }
137 :
138 : std::unique_ptr<AudioFrame>
139 0 : Resampler::resample(std::unique_ptr<AudioFrame>&& in, const AudioFormat& format)
140 : {
141 0 : if (in->pointer()->sample_rate == (int) format.sample_rate
142 0 : && in->pointer()->ch_layout.nb_channels == (int) format.nb_channels
143 0 : && (AVSampleFormat) in->pointer()->format == format.sampleFormat) {
144 0 : return std::move(in);
145 : }
146 0 : auto output = std::make_unique<AudioFrame>(format);
147 0 : resample(in->pointer(), output->pointer());
148 0 : output->has_voice = in->has_voice;
149 0 : return output;
150 0 : }
151 :
152 : std::shared_ptr<AudioFrame>
153 0 : Resampler::resample(std::shared_ptr<AudioFrame>&& in, const AudioFormat& format)
154 : {
155 0 : if (not in) {
156 0 : return {};
157 : }
158 0 : auto inPtr = in->pointer();
159 0 : if (inPtr == nullptr) {
160 0 : return {};
161 : }
162 :
163 0 : if (inPtr->sample_rate == (int) format.sample_rate
164 0 : && inPtr->ch_layout.nb_channels == (int) format.nb_channels
165 0 : && (AVSampleFormat) inPtr->format == format.sampleFormat) {
166 0 : return std::move(in);
167 : }
168 :
169 0 : auto output = std::make_shared<AudioFrame>(format);
170 0 : if (auto outPtr = output->pointer()) {
171 0 : resample(inPtr, outPtr);
172 0 : output->has_voice = in->has_voice;
173 0 : return output;
174 : }
175 0 : return {};
176 0 : }
177 :
178 : } // namespace jami
|