Coverage Report

Created: 2026-08-25 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavutil/log.c
Line
Count
Source
1
/*
2
 * log functions
3
 * Copyright (c) 2003 Michel Bardiaux
4
 *
5
 * This file is part of FFmpeg.
6
 *
7
 * FFmpeg is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with FFmpeg; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
 */
21
22
/**
23
 * @file
24
 * logging functions
25
 */
26
27
#include "config.h"
28
29
#if HAVE_UNISTD_H
30
#include <unistd.h>
31
#endif
32
#if HAVE_IO_H
33
#include <io.h>
34
#endif
35
#include <inttypes.h>
36
#include <stdarg.h>
37
#include <stdatomic.h>
38
#include <stdio.h>
39
#include <stdlib.h>
40
#include <string.h>
41
#include "bprint.h"
42
#include "common.h"
43
#include "internal.h"
44
#include "log.h"
45
#include "thread.h"
46
#include "time.h"
47
#include "time_internal.h"
48
49
static AVMutex mutex = AV_MUTEX_INITIALIZER;
50
51
#define LINE_SZ 1024
52
53
#if HAVE_VALGRIND_VALGRIND_H && CONFIG_VALGRIND_BACKTRACE
54
#include <valgrind/valgrind.h>
55
/* this is the log level at which valgrind will output a full backtrace */
56
#define BACKTRACE_LOGLEVEL AV_LOG_ERROR
57
#endif
58
59
static atomic_int av_log_level = AV_LOG_INFO;
60
static atomic_int av_log_flags = 0;
61
62
0
#define NB_LEVELS 8
63
#if defined(_WIN32) && HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE
64
#include <windows.h>
65
static const uint8_t win_color[16 + AV_CLASS_CATEGORY_NB] = {
66
    [AV_LOG_PANIC  /8] = 12,
67
    [AV_LOG_FATAL  /8] = 12,
68
    [AV_LOG_ERROR  /8] = 12,
69
    [AV_LOG_WARNING/8] = 14,
70
    [AV_LOG_INFO   /8] =  7,
71
    [AV_LOG_VERBOSE/8] = 10,
72
    [AV_LOG_DEBUG  /8] = 10,
73
    [AV_LOG_TRACE  /8] = 8,
74
    [16+AV_CLASS_CATEGORY_NA              ] =  7,
75
    [16+AV_CLASS_CATEGORY_INPUT           ] = 13,
76
    [16+AV_CLASS_CATEGORY_OUTPUT          ] =  5,
77
    [16+AV_CLASS_CATEGORY_MUXER           ] = 13,
78
    [16+AV_CLASS_CATEGORY_DEMUXER         ] =  5,
79
    [16+AV_CLASS_CATEGORY_ENCODER         ] = 11,
80
    [16+AV_CLASS_CATEGORY_DECODER         ] =  3,
81
    [16+AV_CLASS_CATEGORY_FILTER          ] = 10,
82
    [16+AV_CLASS_CATEGORY_BITSTREAM_FILTER] =  9,
83
    [16+AV_CLASS_CATEGORY_SWSCALER        ] =  7,
84
    [16+AV_CLASS_CATEGORY_SWRESAMPLER     ] =  7,
85
    [16+AV_CLASS_CATEGORY_HWDEVICE        ] =  6,
86
    [16+AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT ] = 13,
87
    [16+AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT  ] = 5,
88
    [16+AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT ] = 13,
89
    [16+AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT  ] = 5,
90
    [16+AV_CLASS_CATEGORY_DEVICE_OUTPUT       ] = 13,
91
    [16+AV_CLASS_CATEGORY_DEVICE_INPUT        ] = 5,
92
};
93
94
static int16_t background, attr_orig;
95
static HANDLE con;
96
#endif
97
98
static const uint32_t color[16 + AV_CLASS_CATEGORY_NB] = {
99
    [AV_LOG_PANIC  /8] =  52 << 16 | 196 << 8 | 0x41,
100
    [AV_LOG_FATAL  /8] = 208 <<  8 | 0x41,
101
    [AV_LOG_ERROR  /8] = 196 <<  8 | 0x11,
102
    [AV_LOG_WARNING/8] = 226 <<  8 | 0x03,
103
    [AV_LOG_INFO   /8] = 253 <<  8 | 0x09,
104
    [AV_LOG_VERBOSE/8] =  40 <<  8 | 0x02,
105
    [AV_LOG_DEBUG  /8] =  34 <<  8 | 0x02,
106
    [AV_LOG_TRACE  /8] =  34 <<  8 | 0x07,
107
    [16+AV_CLASS_CATEGORY_NA              ] = 250 << 8 | 0x09,
108
    [16+AV_CLASS_CATEGORY_INPUT           ] = 219 << 8 | 0x15,
109
    [16+AV_CLASS_CATEGORY_OUTPUT          ] = 201 << 8 | 0x05,
110
    [16+AV_CLASS_CATEGORY_MUXER           ] = 213 << 8 | 0x15,
111
    [16+AV_CLASS_CATEGORY_DEMUXER         ] = 207 << 8 | 0x05,
112
    [16+AV_CLASS_CATEGORY_ENCODER         ] =  51 << 8 | 0x16,
113
    [16+AV_CLASS_CATEGORY_DECODER         ] =  39 << 8 | 0x06,
114
    [16+AV_CLASS_CATEGORY_FILTER          ] = 155 << 8 | 0x12,
115
    [16+AV_CLASS_CATEGORY_BITSTREAM_FILTER] = 192 << 8 | 0x14,
116
    [16+AV_CLASS_CATEGORY_SWSCALER        ] = 153 << 8 | 0x14,
117
    [16+AV_CLASS_CATEGORY_SWRESAMPLER     ] = 147 << 8 | 0x14,
118
    [16+AV_CLASS_CATEGORY_HWDEVICE        ] = 214 << 8 | 0x13,
119
    [16+AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT ] = 213 << 8 | 0x15,
120
    [16+AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT  ] = 207 << 8 | 0x05,
121
    [16+AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT ] = 213 << 8 | 0x15,
122
    [16+AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT  ] = 207 << 8 | 0x05,
123
    [16+AV_CLASS_CATEGORY_DEVICE_OUTPUT       ] = 213 << 8 | 0x15,
124
    [16+AV_CLASS_CATEGORY_DEVICE_INPUT        ] = 207 << 8 | 0x05,
125
};
126
127
static int use_color = -1;
128
129
#if defined(_WIN32) && HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE
130
static void win_console_puts(const char *str)
131
{
132
    const uint8_t *q = str;
133
    uint16_t line[LINE_SZ];
134
135
    while (*q) {
136
        uint16_t *buf = line;
137
        DWORD nb_chars = 0;
138
        DWORD written;
139
140
        while (*q && nb_chars < LINE_SZ - 1) {
141
            uint32_t ch;
142
            uint16_t tmp;
143
144
            GET_UTF8(ch, *q ? *q++ : 0, ch = 0xfffd; goto continue_on_invalid;)
145
continue_on_invalid:
146
            PUT_UTF16(ch, tmp, *buf++ = tmp; nb_chars++;)
147
        }
148
149
        WriteConsoleW(con, line, nb_chars, &written, NULL);
150
    }
151
}
152
#endif
153
154
static void check_color_terminal(void)
155
0
{
156
0
    char *term = getenv("TERM");
157
158
#if defined(_WIN32) && HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE
159
    CONSOLE_SCREEN_BUFFER_INFO con_info;
160
    DWORD dummy;
161
    con = GetStdHandle(STD_ERROR_HANDLE);
162
    if (con != INVALID_HANDLE_VALUE && !GetConsoleMode(con, &dummy))
163
        con = INVALID_HANDLE_VALUE;
164
    if (con != INVALID_HANDLE_VALUE) {
165
        GetConsoleScreenBufferInfo(con, &con_info);
166
        attr_orig  = con_info.wAttributes;
167
        background = attr_orig & 0xF0;
168
    }
169
#endif
170
171
0
    if (getenv("AV_LOG_FORCE_NOCOLOR")) {
172
0
        use_color = 0;
173
0
    } else if (getenv("AV_LOG_FORCE_COLOR")) {
174
0
        use_color = 1;
175
0
    } else {
176
#if defined(_WIN32) && HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE
177
        use_color = (con != INVALID_HANDLE_VALUE);
178
#elif HAVE_ISATTY
179
0
        use_color = (term && isatty(2));
180
#else
181
        use_color = 0;
182
#endif
183
0
    }
184
185
0
    if (getenv("AV_LOG_FORCE_256COLOR") || term && strstr(term, "256color"))
186
0
        use_color *= 256;
187
0
}
188
189
static void ansi_fputs(int level, int tint, const char *str, int local_use_color)
190
0
{
191
0
    if (local_use_color == 1) {
192
0
        fprintf(stderr,
193
0
                "\033[%"PRIu32";3%"PRIu32"m%s\033[0m",
194
0
                (color[level] >> 4) & 15,
195
0
                color[level] & 15,
196
0
                str);
197
0
    } else if (tint && use_color == 256) {
198
0
        fprintf(stderr,
199
0
                "\033[48;5;%"PRIu32"m\033[38;5;%dm%s\033[0m",
200
0
                (color[level] >> 16) & 0xff,
201
0
                tint,
202
0
                str);
203
0
    } else if (local_use_color == 256) {
204
0
        fprintf(stderr,
205
0
                "\033[48;5;%"PRIu32"m\033[38;5;%"PRIu32"m%s\033[0m",
206
0
                (color[level] >> 16) & 0xff,
207
0
                (color[level] >> 8) & 0xff,
208
0
                str);
209
0
    } else
210
0
        fputs(str, stderr);
211
0
}
212
213
static void colored_fputs(int level, int tint, const char *str)
214
0
{
215
0
    int local_use_color;
216
0
    if (!*str)
217
0
        return;
218
219
0
    if (use_color < 0)
220
0
        check_color_terminal();
221
222
0
    if (level == AV_LOG_INFO/8) local_use_color = 0;
223
0
    else                        local_use_color = use_color;
224
225
#if defined(_WIN32) && HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE
226
    if (con != INVALID_HANDLE_VALUE) {
227
        if (local_use_color)
228
            SetConsoleTextAttribute(con, background | win_color[level]);
229
        win_console_puts(str);
230
        if (local_use_color)
231
            SetConsoleTextAttribute(con, attr_orig);
232
    } else {
233
        ansi_fputs(level, tint, str, local_use_color);
234
    }
235
#else
236
0
    ansi_fputs(level, tint, str, local_use_color);
237
0
#endif
238
239
0
}
240
241
const char *av_default_item_name(void *ptr)
242
0
{
243
0
    return (*(AVClass **) ptr)->class_name;
244
0
}
245
246
AVClassCategory av_default_get_category(void *ptr)
247
0
{
248
0
    return (*(AVClass **) ptr)->category;
249
0
}
250
251
0
static void sanitize(uint8_t *line){
252
0
    while(*line){
253
0
        if(*line < 0x08 || (*line > 0x0D && *line < 0x20))
254
0
            *line='?';
255
0
        line++;
256
0
    }
257
0
}
258
259
0
static int get_category(void *ptr){
260
0
    AVClass *avc = *(AVClass **) ptr;
261
0
    if(    !avc
262
0
        || (avc->version&0xFF)<100
263
0
        ||  avc->version < (51 << 16 | 59 << 8)
264
0
        ||  avc->category >= AV_CLASS_CATEGORY_NB) return AV_CLASS_CATEGORY_NA + 16;
265
266
0
    if(avc->get_category)
267
0
        return avc->get_category(ptr) + 16;
268
269
0
    return avc->category + 16;
270
0
}
271
272
static const char *get_level_str(int level)
273
0
{
274
0
    switch (level) {
275
0
    case AV_LOG_QUIET:
276
0
        return "quiet";
277
0
    case AV_LOG_DEBUG:
278
0
        return "debug";
279
0
    case AV_LOG_TRACE:
280
0
        return "trace";
281
0
    case AV_LOG_VERBOSE:
282
0
        return "verbose";
283
0
    case AV_LOG_INFO:
284
0
        return "info";
285
0
    case AV_LOG_WARNING:
286
0
        return "warning";
287
0
    case AV_LOG_ERROR:
288
0
        return "error";
289
0
    case AV_LOG_FATAL:
290
0
        return "fatal";
291
0
    case AV_LOG_PANIC:
292
0
        return "panic";
293
0
    default:
294
0
        return "";
295
0
    }
296
0
}
297
298
static const char *item_name(void *obj, const AVClass *cls)
299
0
{
300
0
    return (cls->item_name ? cls->item_name : av_default_item_name)(obj);
301
0
}
302
303
static void format_date_now(AVBPrint* bp_time, int include_date)
304
0
{
305
0
    struct tm *ptm, tmbuf;
306
0
    const int64_t time_us = av_gettime();
307
0
    const int64_t time_ms = time_us / 1000;
308
0
    const time_t time_s   = time_ms / 1000;
309
0
    const int millisec    = time_ms - (time_s * 1000);
310
0
    ptm                   = localtime_r(&time_s, &tmbuf);
311
0
    if (ptm) {
312
0
        if (include_date)
313
0
            av_bprint_strftime(bp_time, "%Y-%m-%d ", ptm);
314
315
0
        av_bprint_strftime(bp_time, "%H:%M:%S", ptm);
316
0
        av_bprintf(bp_time, ".%03d ", millisec);
317
0
    }
318
0
}
319
320
static void format_line(void *avcl, int level, const char *fmt, va_list vl,
321
                        AVBPrint part[5], int *print_prefix, int type[2])
322
0
{
323
0
    AVClass* avc = avcl ? *(AVClass **) avcl : NULL;
324
0
    av_bprint_init(part+0, 0, AV_BPRINT_SIZE_AUTOMATIC);
325
0
    av_bprint_init(part+1, 0, AV_BPRINT_SIZE_AUTOMATIC);
326
0
    av_bprint_init(part+2, 0, AV_BPRINT_SIZE_AUTOMATIC);
327
0
    av_bprint_init(part+3, 0, 65536);
328
0
    av_bprint_init(part+4, 0, AV_BPRINT_SIZE_AUTOMATIC);
329
0
    int flags = atomic_load_explicit(&av_log_flags, memory_order_relaxed);
330
331
0
    if(type) type[0] = type[1] = AV_CLASS_CATEGORY_NA + 16;
332
0
    if (*print_prefix && avc) {
333
0
        if (avc->parent_log_context_offset) {
334
0
            AVClass** parent = *(AVClass ***) (((uint8_t *) avcl) +
335
0
                                   avc->parent_log_context_offset);
336
0
            if (parent && *parent) {
337
0
                av_bprintf(part+0, "[%s @ %p] ",
338
0
                           item_name(parent, *parent), parent);
339
0
                if(type) type[0] = get_category(parent);
340
0
            }
341
0
        }
342
0
        av_bprintf(part+1, "[%s @ %p] ",
343
0
                   item_name(avcl, avc), avcl);
344
0
        if(type) type[1] = get_category(avcl);
345
0
    }
346
347
0
    if (*print_prefix && (level > AV_LOG_QUIET) && (flags & (AV_LOG_PRINT_TIME | AV_LOG_PRINT_DATETIME)))
348
0
        format_date_now(&part[4], flags & AV_LOG_PRINT_DATETIME);
349
350
0
    if (*print_prefix && (level > AV_LOG_QUIET) && (flags & AV_LOG_PRINT_LEVEL))
351
0
        av_bprintf(part+2, "[%s] ", get_level_str(level));
352
353
0
    av_vbprintf(part+3, fmt, vl);
354
355
0
    if(*part[0].str || *part[1].str || *part[2].str || *part[3].str) {
356
0
        char lastc = part[3].len && part[3].len <= part[3].size ? part[3].str[part[3].len - 1] : 0;
357
0
        *print_prefix = lastc == '\n' || lastc == '\r';
358
0
    }
359
0
}
360
361
void av_log_format_line(void *ptr, int level, const char *fmt, va_list vl,
362
                        char *line, int line_size, int *print_prefix)
363
0
{
364
0
    av_log_format_line2(ptr, level, fmt, vl, line, line_size, print_prefix);
365
0
}
366
367
int av_log_format_line2(void *ptr, int level, const char *fmt, va_list vl,
368
                        char *line, int line_size, int *print_prefix)
369
0
{
370
0
    AVBPrint part[5];
371
0
    int ret;
372
373
0
    format_line(ptr, level, fmt, vl, part, print_prefix, NULL);
374
0
    ret = snprintf(line, line_size, "%s%s%s%s", part[0].str, part[1].str, part[2].str, part[3].str);
375
0
    av_bprint_finalize(part+3, NULL);
376
0
    return ret;
377
0
}
378
379
void av_log_default_callback(void* ptr, int level, const char* fmt, va_list vl)
380
595
{
381
595
    static int print_prefix = 1;
382
595
    static int count;
383
595
    static char prev[LINE_SZ];
384
595
    AVBPrint part[5];
385
595
    char line[LINE_SZ];
386
595
    static int is_atty;
387
595
    int type[2];
388
595
    unsigned tint = 0;
389
390
595
    if (level >= 0) {
391
595
        tint = level & 0xff00;
392
595
        level &= 0xff;
393
595
    }
394
395
595
    if (level > atomic_load_explicit(&av_log_level, memory_order_relaxed))
396
595
        return;
397
0
    ff_mutex_lock(&mutex);
398
399
0
    format_line(ptr, level, fmt, vl, part, &print_prefix, type);
400
0
    snprintf(line, sizeof(line), "%s%s%s%s", part[0].str, part[1].str, part[2].str, part[3].str);
401
402
0
#if HAVE_ISATTY
403
0
    if (!is_atty)
404
0
        is_atty = isatty(2) ? 1 : -1;
405
0
#endif
406
407
0
    if (print_prefix && (atomic_load_explicit(&av_log_flags, memory_order_relaxed) & AV_LOG_SKIP_REPEATED) && !strcmp(line, prev) &&
408
0
        *line && line[strlen(line) - 1] != '\r'){
409
0
        count++;
410
0
        if (is_atty == 1)
411
0
            fprintf(stderr, "    Last message repeated %d times\r", count);
412
0
        goto end;
413
0
    }
414
0
    if (count > 0) {
415
0
        fprintf(stderr, "    Last message repeated %d times\n", count);
416
0
        count = 0;
417
0
    }
418
0
    strcpy(prev, line);
419
420
0
    sanitize(part[4].str);
421
0
    colored_fputs(7, 0, part[4].str);
422
0
    sanitize(part[0].str);
423
0
    colored_fputs(type[0], 0, part[0].str);
424
0
    sanitize(part[1].str);
425
0
    colored_fputs(type[1], 0, part[1].str);
426
0
    sanitize(part[2].str);
427
0
    colored_fputs(av_clip(level >> 3, 0, NB_LEVELS - 1), tint >> 8, part[2].str);
428
0
    sanitize(part[3].str);
429
0
    colored_fputs(av_clip(level >> 3, 0, NB_LEVELS - 1), tint >> 8, part[3].str);
430
431
#if CONFIG_VALGRIND_BACKTRACE
432
    if (level <= BACKTRACE_LOGLEVEL)
433
        VALGRIND_PRINTF_BACKTRACE("%s", "");
434
#endif
435
0
end:
436
0
    av_bprint_finalize(part+3, NULL);
437
0
    ff_mutex_unlock(&mutex);
438
0
}
439
440
static atomic_uintptr_t av_log_callback = (uintptr_t)av_log_default_callback;
441
442
void av_log(void* avcl, int level, const char *fmt, ...)
443
595
{
444
595
    va_list vl;
445
595
    va_start(vl, fmt);
446
595
    av_vlog(avcl, level, fmt, vl);
447
595
    va_end(vl);
448
595
}
449
450
void av_log_once(void* avcl, int initial_level, int subsequent_level, int *state, const char *fmt, ...)
451
0
{
452
0
    va_list vl;
453
0
    va_start(vl, fmt);
454
0
    av_vlog(avcl, *state ? subsequent_level : initial_level, fmt, vl);
455
0
    va_end(vl);
456
0
    *state = 1;
457
0
}
458
459
void av_vlog(void* avcl, int level, const char *fmt, va_list vl)
460
595
{
461
595
    AVClass* avc = avcl ? *(AVClass **) avcl : NULL;
462
595
    void (*log_callback)(void*, int, const char*, va_list) =
463
595
        (void *)atomic_load_explicit(&av_log_callback, memory_order_relaxed);
464
595
    if (avc && avc->version >= (50 << 16 | 15 << 8 | 2) &&
465
595
        avc->log_level_offset_offset && level >= AV_LOG_FATAL)
466
0
        level += *(int *) (((uint8_t *) avcl) + avc->log_level_offset_offset);
467
595
    if (log_callback)
468
595
        log_callback(avcl, level, fmt, vl);
469
595
}
470
471
int av_log_get_level(void)
472
0
{
473
0
    return atomic_load_explicit(&av_log_level, memory_order_relaxed);
474
0
}
475
476
void av_log_set_level(int level)
477
0
{
478
0
    atomic_store_explicit(&av_log_level, level, memory_order_relaxed);
479
0
}
480
481
void av_log_set_flags(int arg)
482
0
{
483
0
    atomic_store_explicit(&av_log_flags, arg, memory_order_relaxed);
484
0
}
485
486
int av_log_get_flags(void)
487
0
{
488
0
    return atomic_load_explicit(&av_log_flags, memory_order_relaxed);
489
0
}
490
491
void av_log_set_callback(void (*callback)(void*, int, const char*, va_list))
492
0
{
493
0
    atomic_store_explicit(&av_log_callback, (uintptr_t)callback, memory_order_relaxed);
494
0
}
495
496
static void missing_feature_sample(int sample, void *avc, const char *msg,
497
                                   va_list argument_list)
498
0
{
499
0
    av_vlog(avc, AV_LOG_WARNING, msg, argument_list);
500
0
    av_log(avc, AV_LOG_WARNING, " is not implemented. Update your FFmpeg "
501
0
           "version to the newest one from Git. If the problem still "
502
0
           "occurs, it means that your file has a feature which has not "
503
0
           "been implemented.\n");
504
0
    if (sample)
505
0
        av_log(avc, AV_LOG_WARNING, "If you want to help, upload a sample "
506
0
               "of this file to https://streams.videolan.org/upload/ "
507
0
               "and contact the ffmpeg-devel mailing list. (ffmpeg-devel@ffmpeg.org)\n");
508
0
}
509
510
void avpriv_request_sample(void *avc, const char *msg, ...)
511
0
{
512
0
    va_list argument_list;
513
514
0
    va_start(argument_list, msg);
515
0
    missing_feature_sample(1, avc, msg, argument_list);
516
0
    va_end(argument_list);
517
0
}
518
519
void avpriv_report_missing_feature(void *avc, const char *msg, ...)
520
0
{
521
0
    va_list argument_list;
522
523
0
    va_start(argument_list, msg);
524
0
    missing_feature_sample(0, avc, msg, argument_list);
525
    va_end(argument_list);
526
0
}