Ring Daemon 16.0.0
Loading...
Searching...
No Matches
resampler.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2004-2025 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
22extern "C" {
23#include <libswresample/swresample.h>
24}
25
26namespace jami {
27
29 : swrCtx_(swr_alloc())
30 , initCount_(0)
31{}
32
34{
35 swr_free(&swrCtx_);
36}
37
38void
39Resampler::reinit(const AVFrame* in, const AVFrame* out)
40{
41 // NOTE swr_set_matrix should be called on an uninitialized context
42 auto swrCtx = swr_alloc();
43 if (!swrCtx) {
44 JAMI_ERR() << "Unable to allocate resampler context";
45 throw std::bad_alloc();
46 }
47
48 av_opt_set_chlayout(swrCtx, "ichl", &in->ch_layout, 0);
49 av_opt_set_int(swrCtx, "isr", in->sample_rate, 0);
50 av_opt_set_sample_fmt(swrCtx, "isf", static_cast<AVSampleFormat>(in->format), 0);
51
52 av_opt_set_chlayout(swrCtx, "ochl", &out->ch_layout, 0);
53 av_opt_set_int(swrCtx, "osr", out->sample_rate, 0);
54 av_opt_set_sample_fmt(swrCtx, "osf", static_cast<AVSampleFormat>(out->format), 0);
55
67 if (in->ch_layout.u.mask == AV_CH_LAYOUT_5POINT1
68 || in->ch_layout.u.mask == AV_CH_LAYOUT_5POINT1_BACK) {
69 // NOTE: MSVC is unable to allocate dynamic size arrays on the stack
70 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 matrix[0][0] = 1;
74 matrix[0][1] = 0;
75 matrix[0][2] = 0.707;
76 matrix[0][3] = 1;
77 matrix[0][4] = 0.707;
78 matrix[0][5] = 0;
79 // R = 1.0*FR + 0.707*FC + 0.707*BR + 1.0*LFE
80 matrix[1][0] = 0;
81 matrix[1][1] = 1;
82 matrix[1][2] = 0.707;
83 matrix[1][3] = 1;
84 matrix[1][4] = 0;
85 matrix[1][5] = 0.707;
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 matrix[0][0] = 1;
91 matrix[0][1] = 1;
92 matrix[0][2] = 1.414;
93 matrix[0][3] = 2;
94 matrix[0][4] = 0.707;
95 matrix[0][5] = 0.707;
97 }
98 }
99
100 if (swr_init(swrCtx) >= 0) {
101 std::swap(swrCtx_, swrCtx);
103 ++initCount_;
104 } else {
105 std::string msg = "Failed to initialize resampler context";
106 JAMI_ERR() << msg;
107 throw std::runtime_error(msg);
108 }
109}
110
111int
113{
114 if (!initCount_)
115 reinit(input, output);
116
117 int ret = swr_convert_frame(swrCtx_, output, input);
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 if (initCount_ > 1) {
123 JAMI_ERROR("Infinite loop detected in audio resampler, please open an issue on https://git.jami.net");
124 throw std::runtime_error("Resampler");
125 }
126 reinit(input, output);
127 return resample(input, output);
128 } else if (ret < 0) {
129 JAMI_ERROR("Failed to resample frame");
130 return -1;
131 }
132
133 // Resampling worked, reset count to 1 so reinit isn't called again
134 initCount_ = 1;
135 return 0;
136}
137
138std::unique_ptr<AudioFrame>
139Resampler::resample(std::unique_ptr<AudioFrame>&& in, const AudioFormat& format)
140{
141 if (in->pointer()->sample_rate == (int) format.sample_rate
142 && in->pointer()->ch_layout.nb_channels == (int) format.nb_channels
143 && (AVSampleFormat) in->pointer()->format == format.sampleFormat) {
144 return std::move(in);
145 }
146 auto output = std::make_unique<AudioFrame>(format);
147 resample(in->pointer(), output->pointer());
148 output->has_voice = in->has_voice;
149 return output;
150}
151
152std::shared_ptr<AudioFrame>
153Resampler::resample(std::shared_ptr<AudioFrame>&& in, const AudioFormat& format)
154{
155 if (not in) {
156 return {};
157 }
158 auto inPtr = in->pointer();
159 if (inPtr == nullptr) {
160 return {};
161 }
162
163 if (inPtr->sample_rate == (int) format.sample_rate
164 && inPtr->ch_layout.nb_channels == (int) format.nb_channels
165 && (AVSampleFormat) inPtr->format == format.sampleFormat) {
166 return std::move(in);
167 }
168
169 auto output = std::make_shared<AudioFrame>(format);
170 if (auto outPtr = output->pointer()) {
172 output->has_voice = in->has_voice;
173 return output;
174 }
175 return {};
176}
177
178} // namespace jami
int resample(const AVFrame *input, AVFrame *output)
Resample a frame.
#define JAMI_ERR(...)
Definition logger.h:218
#define JAMI_ERROR(formatstr,...)
Definition logger.h:228
void emitSignal(Args... args)
Definition ring_signal.h:64
Structure to hold sample rate and channel number associated with audio data.
AVSampleFormat sampleFormat