Line data Source code
1 : /*
2 : * Copyright (C) 2004-2026 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 "audio_frame_resizer.h"
19 : #include "libav_deps.h"
20 : #include "libav_utils.h"
21 : #include "logger.h"
22 : #include <libavutil/frame.h>
23 :
24 : extern "C" {
25 : #include <libavutil/audio_fifo.h>
26 : }
27 :
28 : #include <stdexcept>
29 : #include <utility>
30 :
31 : namespace jami {
32 :
33 767 : AudioFrameResizer::AudioFrameResizer(const AudioFormat& format,
34 : int size,
35 767 : std::function<void(std::shared_ptr<AudioFrame>&&)> cb)
36 767 : : format_(format)
37 767 : , frameSize_(size)
38 767 : , cb_(std::move(cb))
39 767 : , queue_(av_audio_fifo_alloc(format.sampleFormat, static_cast<int>(format.nb_channels), frameSize_))
40 767 : {}
41 :
42 767 : AudioFrameResizer::~AudioFrameResizer()
43 : {
44 767 : av_audio_fifo_free(queue_);
45 767 : }
46 :
47 : int
48 370 : AudioFrameResizer::samples() const
49 : {
50 370 : return av_audio_fifo_size(queue_);
51 : }
52 :
53 : int
54 0 : AudioFrameResizer::frameSize() const
55 : {
56 0 : return frameSize_;
57 : }
58 :
59 : AudioFormat
60 0 : AudioFrameResizer::format() const
61 : {
62 0 : return format_;
63 : }
64 :
65 : void
66 278 : AudioFrameResizer::setFormat(const AudioFormat& format, int size)
67 : {
68 278 : if (size)
69 278 : setFrameSize(size);
70 278 : if (format != format_) {
71 177 : if (auto discarded = samples())
72 4 : JAMI_WARNING("Discarding {} samples", discarded);
73 177 : av_audio_fifo_free(queue_);
74 177 : format_ = format;
75 177 : queue_ = av_audio_fifo_alloc(format.sampleFormat, static_cast<int>(format.nb_channels), frameSize_);
76 : }
77 278 : }
78 :
79 : void
80 278 : AudioFrameResizer::setFrameSize(int frameSize)
81 : {
82 278 : if (frameSize_ != frameSize) {
83 176 : frameSize_ = frameSize;
84 176 : if (cb_)
85 176 : while (auto frame = dequeue())
86 176 : cb_(std::move(frame));
87 : }
88 278 : }
89 :
90 : void
91 5 : AudioFrameResizer::enqueue(std::shared_ptr<AudioFrame>&& frame)
92 : {
93 5 : if (not frame or frame->pointer() == nullptr)
94 0 : return;
95 :
96 5 : int ret = 0;
97 5 : auto* f = frame->pointer();
98 5 : AudioFormat format(f->sample_rate, f->ch_layout.nb_channels, (AVSampleFormat) f->format);
99 5 : if (format != format_) {
100 0 : JAMI_WARNING("Expected {} but got {}", format_.toString(), format.toString());
101 0 : setFormat(format, frameSize_);
102 : }
103 :
104 5 : auto nb_samples = samples();
105 5 : if (cb_ && nb_samples == 0 && f->nb_samples == frameSize_) {
106 1 : nextOutputPts_ = frame->pointer()->pts + frameSize_;
107 1 : cb_(std::move(frame));
108 1 : return; // return if frame was just passed through
109 : }
110 :
111 : // voice activity
112 4 : hasVoice_ = frame->has_voice;
113 :
114 : // queue reallocates itself if need be
115 4 : if ((ret = av_audio_fifo_write(queue_, reinterpret_cast<void**>(f->data), f->nb_samples)) < 0) {
116 0 : JAMI_ERROR("Audio resizer error: {}", libav_utils::getError(ret));
117 0 : throw std::runtime_error("Failed to add audio to frame resizer");
118 : }
119 :
120 4 : if (nextOutputPts_ == 0)
121 3 : nextOutputPts_ = frame->pointer()->pts - nb_samples;
122 :
123 4 : if (cb_)
124 6 : while (auto frame = dequeue())
125 6 : cb_(std::move(frame));
126 : }
127 :
128 : std::shared_ptr<AudioFrame>
129 182 : AudioFrameResizer::dequeue()
130 : {
131 182 : if (samples() < frameSize_)
132 180 : return {};
133 :
134 2 : auto frame = std::make_shared<AudioFrame>(format_, frameSize_);
135 : int ret;
136 2 : if ((ret = av_audio_fifo_read(queue_, reinterpret_cast<void**>(frame->pointer()->data), frameSize_)) < 0) {
137 0 : JAMI_ERROR("Unable to read samples from queue: {}", libav_utils::getError(ret));
138 0 : return {};
139 : }
140 2 : frame->pointer()->pts = nextOutputPts_;
141 2 : frame->has_voice = hasVoice_;
142 2 : nextOutputPts_ += frameSize_;
143 2 : return frame;
144 2 : }
145 :
146 : } // namespace jami
|