Coverage Report

Created: 2026-03-27 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mpv/common/av_log.c
Line
Count
Source
1
/*
2
 * av_log to mp_msg converter
3
 * Copyright (C) 2006 Michael Niedermayer
4
 * Copyright (C) 2009 Uoti Urpala
5
 *
6
 * This file is part of mpv.
7
 *
8
 * mpv is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public
10
 * License as published by the Free Software Foundation; either
11
 * version 2.1 of the License, or (at your option) any later version.
12
 *
13
 * mpv is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public
19
 * License along with mpv.  If not, see <http://www.gnu.org/licenses/>.
20
 */
21
22
#include <stdlib.h>
23
#include <stdio.h>
24
#include <stdbool.h>
25
26
#include "av_log.h"
27
#include "common/common.h"
28
#include "common/global.h"
29
#include "common/msg.h"
30
#include "config.h"
31
#include "misc/bstr.h"
32
#include "osdep/threads.h"
33
34
#include <libavutil/avutil.h>
35
#include <libavutil/ffversion.h>
36
#include <libavutil/log.h>
37
#include <libavutil/version.h>
38
39
#include <libavcodec/avcodec.h>
40
#include <libavcodec/version.h>
41
#include <libavformat/avformat.h>
42
#include <libavformat/version.h>
43
#include <libswresample/swresample.h>
44
#include <libswresample/version.h>
45
#include <libswscale/swscale.h>
46
#include <libswscale/version.h>
47
#include <libavfilter/avfilter.h>
48
#include <libavfilter/version.h>
49
50
#if HAVE_LIBAVDEVICE
51
#include <libavdevice/avdevice.h>
52
#endif
53
54
// Needed because the av_log callback does not provide a library-safe message
55
// callback.
56
static mp_static_mutex log_lock = MP_STATIC_MUTEX_INITIALIZER;
57
static struct mpv_global *log_mpv_instance;
58
static struct mp_log *log_root, *log_decaudio, *log_decvideo, *log_demuxer;
59
static bool log_print_prefix = true;
60
static bstr log_buffer;
61
62
static int av_log_level_to_mp_level(int av_level)
63
16.1M
{
64
16.1M
    if (av_level > AV_LOG_VERBOSE)
65
8.74M
        return MSGL_TRACE;
66
7.36M
    if (av_level > AV_LOG_INFO)
67
1.41M
        return MSGL_DEBUG;
68
5.94M
    if (av_level > AV_LOG_WARNING)
69
304k
        return MSGL_V;
70
5.64M
    if (av_level > AV_LOG_ERROR)
71
644k
        return MSGL_WARN;
72
4.99M
    if (av_level > AV_LOG_FATAL)
73
4.99M
        return MSGL_ERR;
74
708
    return MSGL_FATAL;
75
4.99M
}
76
77
static struct mp_log *get_av_log(void *ptr)
78
16.1M
{
79
16.1M
    if (!ptr)
80
3.18M
        return log_root;
81
82
12.9M
    AVClass *avc = *(AVClass **)ptr;
83
12.9M
    if (!avc) {
84
0
        mp_warn(log_root,
85
0
               "av_log callback called with bad parameters (NULL AVClass).\n"
86
0
               "This is a bug in one of FFmpeg libraries used.\n");
87
0
        return log_root;
88
0
    }
89
90
12.9M
    if (!strcmp(avc->class_name, "AVCodecContext")) {
91
7.86M
        AVCodecContext *s = ptr;
92
7.86M
        if (s->codec) {
93
7.71M
            if (s->codec->type == AVMEDIA_TYPE_AUDIO) {
94
4.93M
                if (av_codec_is_decoder(s->codec))
95
4.93M
                    return log_decaudio;
96
4.93M
            } else if (s->codec->type == AVMEDIA_TYPE_VIDEO) {
97
2.78M
                if (av_codec_is_decoder(s->codec))
98
2.78M
                    return log_decvideo;
99
2.78M
            }
100
7.71M
        }
101
7.86M
    }
102
103
5.21M
    if (!strcmp(avc->class_name, "AVFormatContext")) {
104
2.79M
        AVFormatContext *s = ptr;
105
2.79M
        if (s->iformat)
106
2.79M
            return log_demuxer;
107
2.79M
    }
108
109
2.42M
    return log_root;
110
5.21M
}
111
112
static const char *avclass_item_name(void *obj, const AVClass *avc)
113
12.9M
{
114
12.9M
    return (avc->item_name ? avc->item_name : av_default_item_name)(obj);
115
12.9M
}
116
117
static void mp_msg_av_log_callback(void *ptr, int level, const char *fmt,
118
                                   va_list vl)
119
16.1M
{
120
16.1M
    AVClass *avc = ptr ? *(AVClass **)ptr : NULL;
121
16.1M
    int mp_level = av_log_level_to_mp_level(level);
122
123
    // Note: mp_log is thread-safe, but destruction of the log instances is not.
124
16.1M
    mp_mutex_lock(&log_lock);
125
126
16.1M
    if (!log_mpv_instance) {
127
0
        mp_mutex_unlock(&log_lock);
128
        // Fallback to stderr
129
0
        vfprintf(stderr, fmt, vl);
130
0
        return;
131
0
    }
132
133
16.1M
    struct mp_log *log = get_av_log(ptr);
134
135
16.1M
    if (mp_msg_test(log, mp_level)) {
136
16.1M
        log_buffer.len = 0;
137
16.1M
        bstr_xappend_vasprintf(log_root, &log_buffer, fmt, vl);
138
16.1M
        if (!log_buffer.len)
139
0
            goto done;
140
16.1M
        const char *prefix = avc ? avclass_item_name(ptr, avc) : NULL;
141
16.1M
        if (log_print_prefix && prefix) {
142
11.0M
            mp_msg(log, mp_level, "%s: %.*s", prefix, BSTR_P(log_buffer));
143
11.0M
        } else {
144
5.05M
            mp_msg(log, mp_level, "%.*s", BSTR_P(log_buffer));
145
5.05M
        }
146
16.1M
        log_print_prefix = log_buffer.start[log_buffer.len - 1] == '\n';
147
16.1M
    }
148
149
16.1M
done:
150
16.1M
    mp_mutex_unlock(&log_lock);
151
16.1M
}
152
153
void init_libav(struct mpv_global *global)
154
141k
{
155
141k
    mp_mutex_lock(&log_lock);
156
141k
    if (!log_mpv_instance) {
157
141k
        log_mpv_instance = global;
158
141k
        log_root = mp_log_new(NULL, global->log, "ffmpeg");
159
141k
        log_decaudio = mp_log_new(log_root, log_root, "audio");
160
141k
        log_decvideo = mp_log_new(log_root, log_root, "video");
161
141k
        log_demuxer = mp_log_new(log_root, log_root, "demuxer");
162
141k
        log_buffer = (bstr){0};
163
141k
        av_log_set_callback(mp_msg_av_log_callback);
164
141k
    }
165
141k
    mp_mutex_unlock(&log_lock);
166
167
141k
    avformat_network_init();
168
169
141k
#if HAVE_LIBAVDEVICE
170
141k
    avdevice_register_all();
171
141k
#endif
172
141k
}
173
174
void uninit_libav(struct mpv_global *global)
175
141k
{
176
141k
    mp_mutex_lock(&log_lock);
177
141k
    if (log_mpv_instance == global) {
178
141k
        av_log_set_callback(av_log_default_callback);
179
141k
        log_mpv_instance = NULL;
180
141k
        talloc_free(log_root);
181
141k
    }
182
141k
    mp_mutex_unlock(&log_lock);
183
141k
}
184
185
1.79M
#define V(x) AV_VERSION_MAJOR(x), \
186
1.79M
             AV_VERSION_MINOR(x), \
187
1.79M
             AV_VERSION_MICRO(x)
188
189
struct lib {
190
    const char *name;
191
    unsigned buildv;
192
    unsigned runv;
193
};
194
195
void check_library_versions(struct mp_log *log, int v)
196
256k
{
197
256k
    const struct lib libs[] = {
198
256k
        {"libavcodec",    LIBAVCODEC_VERSION_INT,    avcodec_version()},
199
256k
#if HAVE_LIBAVDEVICE
200
256k
        {"libavdevice",   LIBAVDEVICE_VERSION_INT,   avdevice_version()},
201
256k
#endif
202
256k
        {"libavfilter",   LIBAVFILTER_VERSION_INT,   avfilter_version()},
203
256k
        {"libavformat",   LIBAVFORMAT_VERSION_INT,   avformat_version()},
204
256k
        {"libavutil",     LIBAVUTIL_VERSION_INT,     avutil_version()},
205
256k
        {"libswresample", LIBSWRESAMPLE_VERSION_INT, swresample_version()},
206
256k
        {"libswscale",    LIBSWSCALE_VERSION_INT,    swscale_version()},
207
256k
    };
208
209
256k
    const char *runtime_version = av_version_info();
210
256k
    mp_msg(log, v, "FFmpeg version: %s", FFMPEG_VERSION);
211
256k
    if (strcmp(runtime_version, FFMPEG_VERSION))
212
0
        mp_msg(log, v, " (runtime %s)", runtime_version);
213
256k
    mp_msg(log, v, "\n");
214
256k
    mp_msg(log, v, "FFmpeg library versions:\n");
215
216
2.05M
    for (int n = 0; n < MP_ARRAY_SIZE(libs); n++) {
217
1.79M
        const struct lib *l = &libs[n];
218
1.79M
        mp_msg(log, v, "   %-15s %d.%d.%d", l->name, V(l->buildv));
219
1.79M
        if (l->buildv != l->runv)
220
0
            mp_msg(log, v, " (runtime %d.%d.%d)", V(l->runv));
221
1.79M
        mp_msg(log, v, "\n");
222
1.79M
        if (l->buildv > l->runv ||
223
1.79M
            AV_VERSION_MAJOR(l->buildv) != AV_VERSION_MAJOR(l->runv))
224
0
        {
225
0
            mp_fatal(log, "%s: build version %d.%d.%d incompatible with runtime version %d.%d.%d\n",
226
0
                     l->name, V(l->buildv), V(l->runv));
227
0
            exit(1);
228
0
        }
229
1.79M
    }
230
256k
}
231
232
#undef V