Ring Daemon 16.0.0
Loading...
Searching...
No Matches
libav_utils.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" // MUST BE INCLUDED FIRST
19
20#ifdef HAVE_CONFIG_H
21#include "config.h"
22#endif
23#include "video/video_base.h"
24#include "logger.h"
25
26#include <vector>
27#include <algorithm>
28#include <string>
29#include <iostream>
30#include <thread>
31#include <mutex>
32#include <exception>
33#include <ciso646> // fix windows compiler bug
34
35extern "C" {
36#if LIBAVUTIL_VERSION_MAJOR < 56
37AVFrameSideData*
38av_frame_new_side_data_from_buf(AVFrame* frame, enum AVFrameSideDataType type, AVBufferRef* buf)
39{
40 auto side_data = av_frame_new_side_data(frame, type, 0);
41 av_buffer_unref(&side_data->buf);
42 side_data->buf = buf;
43 side_data->data = side_data->buf->data;
44 side_data->size = side_data->buf->size;
45 return side_data;
46}
47#endif
48}
49
50namespace jami {
51namespace libav_utils {
52
57{
58 if (codec->sample_fmts)
59 for (int i = 0; i < preferred_formats_count; ++i) {
60 for (auto it = codec->sample_fmts; *it != -1; ++it) {
61 if (*it == preferred_formats[i])
62 return preferred_formats[i];
63 }
64 }
65 return AV_SAMPLE_FMT_NONE;
66}
67
85
86#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
87// protect libav/ffmpeg access
88static int
89avcodecManageMutex(void** data, enum AVLockOp op)
90{
91 auto mutex = reinterpret_cast<std::mutex**>(data);
92 int ret = 0;
93 switch (op) {
94 case AV_LOCK_CREATE:
95 try {
96 *mutex = new std::mutex;
97 } catch (const std::bad_alloc& e) {
98 return AVERROR(ENOMEM);
99 }
100 break;
101 case AV_LOCK_OBTAIN:
102 (*mutex)->lock();
103 break;
104 case AV_LOCK_RELEASE:
105 (*mutex)->unlock();
106 break;
107 case AV_LOCK_DESTROY:
108 delete *mutex;
109 *mutex = nullptr;
110 break;
111 default:
112#ifdef AVERROR_BUG
113 return AVERROR_BUG;
114#else
115 break;
116#endif
117 }
118 return AVERROR(ret);
119}
120#endif
121
122static constexpr const char* AVLOGLEVEL = "AVLOGLEVEL";
123
124static void
126{
127 char* envvar = getenv(AVLOGLEVEL);
128 signed level = AV_LOG_WARNING;
129
130 if (envvar != nullptr) {
132 level = std::max(AV_LOG_QUIET, std::min(level, AV_LOG_DEBUG));
133 }
135}
136
137#ifdef __ANDROID__
138static void
139androidAvLogCb(void* ptr, int level, const char* fmt, va_list vl)
140{
141 if (level > av_log_get_level())
142 return;
143
144 char line[1024];
145 int print_prefix = 1;
146 int android_level;
147 va_list vl2;
148 va_copy(vl2, vl);
150 va_end(vl2);
151
152 // replace unprintable characters with '?'
153 int idx = 0;
154 while (line[idx]) {
155 if (line[idx] < 0x08 || (line[idx] > 0x0D && line[idx] < 0x20))
156 line[idx] = '?';
157 ++idx;
158 }
159
160 switch (level) {
161 case AV_LOG_QUIET:
163 break;
164 case AV_LOG_PANIC:
166 break;
167 case AV_LOG_FATAL:
169 break;
170 case AV_LOG_ERROR:
172 break;
173 case AV_LOG_WARNING:
175 break;
176 case AV_LOG_INFO:
178 break;
179 case AV_LOG_VERBOSE:
181 break;
182 case AV_LOG_DEBUG:
184 break;
185 case AV_LOG_TRACE:
187 break;
188 default:
190 break;
191 }
192 __android_log_print(android_level, "FFmpeg", "%s", line);
193}
194#endif
195
196static void
198{
199#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100)
201#endif
204#if LIBAVFILTER_VERSION_INT < AV_VERSION_INT(7, 13, 100)
206#endif
207
208#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
210#endif
211
213
214#ifdef __ANDROID__
215 // android doesn't like stdout and stderr :(
217#endif
218}
219
220static std::once_flag already_called;
221
222void
224{
225 std::call_once(already_called, init_once);
226}
227
228bool
230{
232 return false;
233
234 /* handle formats that do not use all planes */
235 unsigned used_bit_mask = (1u << desc.nb_components) - 1;
236 for (unsigned i = 0; i < desc.nb_components; ++i)
237 used_bit_mask &= ~(1u << desc.comp[i].plane);
238
239 return not used_bit_mask;
240}
241
242std::string
244{
245 std::string ret(AV_ERROR_MAX_STRING_SIZE, '\0');
246 av_strerror(err, (char*) ret.data(), ret.size());
247 return ret;
248}
249
250const char*
251getDictValue(const AVDictionary* d, const std::string& key, int flags)
252{
253 auto kv = av_dict_get(d, key.c_str(), nullptr, flags);
254 if (kv)
255 return kv->value;
256 else
257 return "";
258}
259
260void
261setDictValue(AVDictionary** d, const std::string& key, const std::string& value, int flags)
262{
263 av_dict_set(d, key.c_str(), value.c_str(), flags);
264}
265
266void
268{
269 const AVPixelFormat format = static_cast<AVPixelFormat>(frame->format);
270 const int planes = av_pix_fmt_count_planes(format);
271 // workaround for casting pointers to different sizes
272 // on 64 bit machines: sizeof(ptrdiff_t) != sizeof(int)
274 for (int i = 0; i < planes; ++i)
275 linesizes[i] = frame->linesize[i];
276 int ret = av_image_fill_black(frame->data,
277 linesizes,
278 format,
279 frame->color_range,
280 frame->width,
281 frame->height);
282 if (ret < 0) {
283 JAMI_ERR() << "Failed to blacken frame";
284 }
285}
286
287void
289{
290 int ret = av_samples_set_silence(frame->extended_data,
291 0,
292 frame->nb_samples,
293 frame->ch_layout.nb_channels,
294 (AVSampleFormat) frame->format);
295 if (ret < 0)
296 JAMI_ERR() << "Failed to fill frame with silence";
297}
298
299} // namespace libav_utils
300} // 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:218
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)
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 ring_signal.h:64