Ring Daemon
Loading...
Searching...
No Matches
libav_utils.cpp
Go to the documentation of this file.
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 "libav_deps.h" // MUST BE INCLUDED FIRST
19#include "string_utils.h"
20
21#ifdef HAVE_CONFIG_H
22#include "config.h"
23#endif
24#include "logger.h"
25
26#include <algorithm>
27#include <string>
28#include <mutex>
29
30extern "C" {
31#if LIBAVUTIL_VERSION_MAJOR < 56
32AVFrameSideData*
33av_frame_new_side_data_from_buf(AVFrame* frame, enum AVFrameSideDataType type, AVBufferRef* buf)
34{
35 auto side_data = av_frame_new_side_data(frame, type, 0);
36 av_buffer_unref(&side_data->buf);
37 side_data->buf = buf;
38 side_data->data = side_data->buf->data;
39 side_data->size = side_data->buf->size;
40 return side_data;
41}
42#endif
43}
44
45namespace jami {
46namespace libav_utils {
47
50{
51 if (codec->sample_fmts)
52 for (int i = 0; i < preferred_formats_count; ++i) {
53 for (const auto* it = codec->sample_fmts; *it != -1; ++it) {
54 if (*it == preferred_formats[i])
55 return preferred_formats[i];
56 }
57 }
58 return AV_SAMPLE_FMT_NONE;
59}
60
76
77#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
78// protect libav/ffmpeg access
79static int
80avcodecManageMutex(void** data, enum AVLockOp op)
81{
82 auto mutex = reinterpret_cast<std::mutex**>(data);
83 int ret = 0;
84 switch (op) {
85 case AV_LOCK_CREATE:
86 try {
87 *mutex = new std::mutex;
88 } catch (const std::bad_alloc& e) {
89 return AVERROR(ENOMEM);
90 }
91 break;
92 case AV_LOCK_OBTAIN:
93 (*mutex)->lock();
94 break;
95 case AV_LOCK_RELEASE:
96 (*mutex)->unlock();
97 break;
98 case AV_LOCK_DESTROY:
99 delete *mutex;
100 *mutex = nullptr;
101 break;
102 default:
103#ifdef AVERROR_BUG
104 return AVERROR_BUG;
105#else
106 break;
107#endif
108 }
109 return AVERROR(ret);
110}
111#endif
112
113static constexpr const char* AVLOGLEVEL = "AVLOGLEVEL";
114
115static void
117{
118 char* envvar = getenv(AVLOGLEVEL);
119 signed level = AV_LOG_WARNING;
120
121 if (envvar != nullptr) {
123 level = std::max(AV_LOG_QUIET, std::min(level, AV_LOG_DEBUG));
124 }
126}
127
128#ifdef __ANDROID__
129static void
130androidAvLogCb(void* ptr, int level, const char* fmt, va_list vl)
131{
132 if (level > av_log_get_level())
133 return;
134
135 char line[1024];
136 int print_prefix = 1;
137 int android_level;
138 va_list vl2;
139 va_copy(vl2, vl);
141 va_end(vl2);
142
143 // replace unprintable characters with '?'
144 int idx = 0;
145 while (line[idx]) {
146 if (line[idx] < 0x08 || (line[idx] > 0x0D && line[idx] < 0x20))
147 line[idx] = '?';
148 ++idx;
149 }
150
151 switch (level) {
152 case AV_LOG_QUIET:
154 break;
155 case AV_LOG_PANIC:
157 break;
158 case AV_LOG_FATAL:
160 break;
161 case AV_LOG_ERROR:
163 break;
164 case AV_LOG_WARNING:
166 break;
167 case AV_LOG_INFO:
169 break;
170 case AV_LOG_VERBOSE:
172 break;
173 case AV_LOG_DEBUG:
175 break;
176 case AV_LOG_TRACE:
178 break;
179 default:
181 break;
182 }
183 __android_log_print(android_level, "FFmpeg", "%s", line);
184}
185#endif
186
187static void
189{
190#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100)
192#endif
195#if LIBAVFILTER_VERSION_INT < AV_VERSION_INT(7, 13, 100)
197#endif
198
199#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
201#endif
202
204
205#ifdef __ANDROID__
206 // android doesn't like stdout and stderr :(
208#endif
209}
210
211static std::once_flag already_called;
212
213void
215{
216 std::call_once(already_called, init_once);
217}
218
219bool
221{
223 return false;
224
225 /* handle formats that do not use all planes */
226 unsigned used_bit_mask = (1u << desc.nb_components) - 1;
227 for (unsigned i = 0; i < desc.nb_components; ++i)
228 used_bit_mask &= ~(1u << desc.comp[i].plane);
229
230 return not used_bit_mask;
231}
232
233std::string
235{
236 std::string ret(AV_ERROR_MAX_STRING_SIZE, '\0');
237 av_strerror(err, (char*) ret.data(), ret.size());
238 return ret;
239}
240
241const char*
242getDictValue(const AVDictionary* d, const std::string& key, int flags)
243{
244 auto* kv = av_dict_get(d, key.c_str(), nullptr, flags);
245 if (kv)
246 return kv->value;
247 else
248 return "";
249}
250
251void
252setDictValue(AVDictionary** d, const std::string& key, const std::string& value, int flags)
253{
254 av_dict_set(d, key.c_str(), value.c_str(), flags);
255}
256
257void
259{
260 const AVPixelFormat format = static_cast<AVPixelFormat>(frame->format);
261 const int planes = av_pix_fmt_count_planes(format);
262 // workaround for casting pointers to different sizes
263 // on 64 bit machines: sizeof(ptrdiff_t) != sizeof(int)
265 for (int i = 0; i < planes; ++i)
266 linesizes[i] = frame->linesize[i];
267 int ret = av_image_fill_black(frame->data, linesizes, format, frame->color_range, frame->width, frame->height);
268 if (ret < 0) {
269 JAMI_ERR() << "Failed to blacken frame";
270 }
271}
272
273void
275{
276 int ret = av_samples_set_silence(frame->extended_data,
277 0,
278 frame->nb_samples,
279 frame->ch_layout.nb_channels,
280 (AVSampleFormat) frame->format);
281 if (ret < 0)
282 JAMI_ERR() << "Failed to fill frame with silence";
283}
284
287{
288 return AudioFormat {static_cast<unsigned>(frame->sample_rate),
289 static_cast<unsigned>(frame->ch_layout.nb_channels),
290 static_cast<AVSampleFormat>(frame->format)};
291}
292
293} // namespace libav_utils
294} // namespace jami
AVFrameSideData * av_frame_new_side_data_from_buf(AVFrame *frame, enum AVFrameSideDataType type, AVBufferRef *buf)
void av_buffer_unref(AVBufferRef **buf)
#define JAMI_ERR(...)
Definition logger.h:230
static std::once_flag already_called
AVSampleFormat choose_sample_fmt(const AVCodec *codec, const AVSampleFormat *preferred_formats, int preferred_formats_count)
void fillWithBlack(AVFrame *frame)
const char * getDictValue(const AVDictionary *d, const std::string &key, int flags)
void setDictValue(AVDictionary **d, const std::string &key, const std::string &value, int flags)
AVSampleFormat choose_sample_fmt_default(const AVCodec *codec, AVSampleFormat defaultFormat)
static void init_once()
void fillWithSilence(AVFrame *frame)
AudioFormat getFormat(const AVFrame *frame)
bool is_yuv_planar(const AVPixFmtDescriptor &desc)
std::string getError(int err)
static void setAvLogLevel()
static constexpr const char * AVLOGLEVEL
void emitSignal(Args... args)
Definition jami_signal.h:64
Structure to hold sample rate and channel number associated with audio data.