summaryrefslogtreecommitdiffstats
path: root/common/av_log.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--common/av_log.c215
1 files changed, 215 insertions, 0 deletions
diff --git a/common/av_log.c b/common/av_log.c
new file mode 100644
index 0000000..b6bad04
--- /dev/null
+++ b/common/av_log.c
@@ -0,0 +1,215 @@
+/*
+ * av_log to mp_msg converter
+ * Copyright (C) 2006 Michael Niedermayer
+ * Copyright (C) 2009 Uoti Urpala
+ *
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+
+#include "av_log.h"
+#include "common/common.h"
+#include "common/global.h"
+#include "common/msg.h"
+#include "config.h"
+#include "osdep/threads.h"
+
+#include <libavutil/avutil.h>
+#include <libavutil/log.h>
+#include <libavutil/version.h>
+
+#include <libavcodec/avcodec.h>
+#include <libavcodec/version.h>
+#include <libavformat/avformat.h>
+#include <libavformat/version.h>
+#include <libswresample/swresample.h>
+#include <libswresample/version.h>
+#include <libswscale/swscale.h>
+#include <libswscale/version.h>
+#include <libavfilter/avfilter.h>
+#include <libavfilter/version.h>
+
+#if HAVE_LIBAVDEVICE
+#include <libavdevice/avdevice.h>
+#endif
+
+// Needed because the av_log callback does not provide a library-safe message
+// callback.
+static mp_static_mutex log_lock = MP_STATIC_MUTEX_INITIALIZER;
+static struct mpv_global *log_mpv_instance;
+static struct mp_log *log_root, *log_decaudio, *log_decvideo, *log_demuxer;
+static bool log_print_prefix = true;
+
+static int av_log_level_to_mp_level(int av_level)
+{
+ if (av_level > AV_LOG_VERBOSE)
+ return MSGL_TRACE;
+ if (av_level > AV_LOG_INFO)
+ return MSGL_DEBUG;
+ if (av_level > AV_LOG_WARNING)
+ return MSGL_V;
+ if (av_level > AV_LOG_ERROR)
+ return MSGL_WARN;
+ if (av_level > AV_LOG_FATAL)
+ return MSGL_ERR;
+ return MSGL_FATAL;
+}
+
+static struct mp_log *get_av_log(void *ptr)
+{
+ if (!ptr)
+ return log_root;
+
+ AVClass *avc = *(AVClass **)ptr;
+ if (!avc) {
+ mp_warn(log_root,
+ "av_log callback called with bad parameters (NULL AVClass).\n"
+ "This is a bug in one of Libav/FFmpeg libraries used.\n");
+ return log_root;
+ }
+
+ if (!strcmp(avc->class_name, "AVCodecContext")) {
+ AVCodecContext *s = ptr;
+ if (s->codec) {
+ if (s->codec->type == AVMEDIA_TYPE_AUDIO) {
+ if (av_codec_is_decoder(s->codec))
+ return log_decaudio;
+ } else if (s->codec->type == AVMEDIA_TYPE_VIDEO) {
+ if (av_codec_is_decoder(s->codec))
+ return log_decvideo;
+ }
+ }
+ }
+
+ if (!strcmp(avc->class_name, "AVFormatContext")) {
+ AVFormatContext *s = ptr;
+ if (s->iformat)
+ return log_demuxer;
+ }
+
+ return log_root;
+}
+
+static void mp_msg_av_log_callback(void *ptr, int level, const char *fmt,
+ va_list vl)
+{
+ AVClass *avc = ptr ? *(AVClass **)ptr : NULL;
+ int mp_level = av_log_level_to_mp_level(level);
+
+ // Note: mp_log is thread-safe, but destruction of the log instances is not.
+ mp_mutex_lock(&log_lock);
+
+ if (!log_mpv_instance) {
+ mp_mutex_unlock(&log_lock);
+ // Fallback to stderr
+ vfprintf(stderr, fmt, vl);
+ return;
+ }
+
+ struct mp_log *log = get_av_log(ptr);
+
+ if (mp_msg_test(log, mp_level)) {
+ char buffer[4096] = "";
+ int pos = 0;
+ const char *prefix = avc ? avc->item_name(ptr) : NULL;
+ if (log_print_prefix && prefix)
+ pos = snprintf(buffer, sizeof(buffer), "%s: ", prefix);
+ log_print_prefix = fmt[strlen(fmt) - 1] == '\n';
+
+ pos = MPMIN(MPMAX(pos, 0), sizeof(buffer));
+ vsnprintf(buffer + pos, sizeof(buffer) - pos, fmt, vl);
+
+ mp_msg(log, mp_level, "%s", buffer);
+ }
+
+ mp_mutex_unlock(&log_lock);
+}
+
+void init_libav(struct mpv_global *global)
+{
+ mp_mutex_lock(&log_lock);
+ if (!log_mpv_instance) {
+ log_mpv_instance = global;
+ log_root = mp_log_new(NULL, global->log, "ffmpeg");
+ log_decaudio = mp_log_new(log_root, log_root, "audio");
+ log_decvideo = mp_log_new(log_root, log_root, "video");
+ log_demuxer = mp_log_new(log_root, log_root, "demuxer");
+ av_log_set_callback(mp_msg_av_log_callback);
+ }
+ mp_mutex_unlock(&log_lock);
+
+ avformat_network_init();
+
+#if HAVE_LIBAVDEVICE
+ avdevice_register_all();
+#endif
+}
+
+void uninit_libav(struct mpv_global *global)
+{
+ mp_mutex_lock(&log_lock);
+ if (log_mpv_instance == global) {
+ av_log_set_callback(av_log_default_callback);
+ log_mpv_instance = NULL;
+ talloc_free(log_root);
+ }
+ mp_mutex_unlock(&log_lock);
+}
+
+#define V(x) AV_VERSION_MAJOR(x), \
+ AV_VERSION_MINOR(x), \
+ AV_VERSION_MICRO(x)
+
+struct lib {
+ const char *name;
+ unsigned buildv;
+ unsigned runv;
+};
+
+void check_library_versions(struct mp_log *log, int v)
+{
+ const struct lib libs[] = {
+ {"libavutil", LIBAVUTIL_VERSION_INT, avutil_version()},
+ {"libavcodec", LIBAVCODEC_VERSION_INT, avcodec_version()},
+ {"libavformat", LIBAVFORMAT_VERSION_INT, avformat_version()},
+ {"libswscale", LIBSWSCALE_VERSION_INT, swscale_version()},
+ {"libavfilter", LIBAVFILTER_VERSION_INT, avfilter_version()},
+ {"libswresample", LIBSWRESAMPLE_VERSION_INT, swresample_version()},
+ };
+
+ mp_msg(log, v, "FFmpeg version: %s\n", av_version_info());
+ mp_msg(log, v, "FFmpeg library versions:\n");
+
+ for (int n = 0; n < MP_ARRAY_SIZE(libs); n++) {
+ const struct lib *l = &libs[n];
+ mp_msg(log, v, " %-15s %d.%d.%d", l->name, V(l->buildv));
+ if (l->buildv != l->runv)
+ mp_msg(log, v, " (runtime %d.%d.%d)", V(l->runv));
+ mp_msg(log, v, "\n");
+ if (l->buildv > l->runv ||
+ AV_VERSION_MAJOR(l->buildv) != AV_VERSION_MAJOR(l->runv))
+ {
+ fprintf(stderr, "%s: %d.%d.%d -> %d.%d.%d\n",
+ l->name, V(l->buildv), V(l->runv));
+ abort();
+ }
+ }
+}
+
+#undef V