From d57eaa7e30a2faada712b60e75b7b2eb734cca3c Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 21 Dec 2013 20:06:36 +0100 Subject: av_log: mp_msg conversion This is pretty nasty, because FFmpeg/Libav is yet another library with a global message callback. We do something with mutexes trying to get it done, but of course we can't actually solve this problem. If more than one library in a process use FFmpeg/Libav, only one of them will get log messages. --- common/av_log.c | 96 ++++++++++++++++++++++++++++++++++++++------------------- common/av_log.h | 7 ++++- player/main.c | 4 +-- 3 files changed, 73 insertions(+), 34 deletions(-) diff --git a/common/av_log.c b/common/av_log.c index b007062d1e..0ee39b3022 100644 --- a/common/av_log.c +++ b/common/av_log.c @@ -21,11 +21,16 @@ */ #include +#include #include +#include #include "av_log.h" #include "config.h" +#include "common/common.h" +#include "common/global.h" #include "common/msg.h" + #include #include @@ -48,6 +53,19 @@ #include #endif +#if LIBAVCODEC_VERSION_MICRO >= 100 +#define LIB_PREFIX "ffmpeg" +#else +#define LIB_PREFIX "libav" +#endif + +// Needed because the av_log callback does not provide a library-safe message +// callback. +static pthread_mutex_t log_lock = PTHREAD_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) @@ -63,17 +81,17 @@ static int av_log_level_to_mp_level(int av_level) return MSGL_FATAL; } -static int extract_msg_type_from_ctx(void *ptr) +static struct mp_log *get_av_log(void *ptr) { if (!ptr) - return MSGT_FIXME; + return log_root; AVClass *avc = *(AVClass **)ptr; if (!avc) { - mp_msg(MSGT_FIXME, MSGL_WARN, + 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 MSGT_FIXME; + return log_root; } if (!strcmp(avc->class_name, "AVCodecContext")) { @@ -81,59 +99,65 @@ static int extract_msg_type_from_ctx(void *ptr) if (s->codec) { if (s->codec->type == AVMEDIA_TYPE_AUDIO) { if (s->codec->decode) - return MSGT_DECAUDIO; + return log_decaudio; } else if (s->codec->type == AVMEDIA_TYPE_VIDEO) { if (s->codec->decode) - return MSGT_DECVIDEO; + return log_decvideo; } - // FIXME subtitles, encoders - // What msgt for them? There is nothing appropriate... } - return MSGT_FIXME; } if (!strcmp(avc->class_name, "AVFormatContext")) { AVFormatContext *s = ptr; if (s->iformat) - return MSGT_DEMUXER; - else if (s->oformat) - return MSGT_MUXER; - return MSGT_FIXME; + return log_demuxer; } - return MSGT_FIXME; + return log_root; } -#if LIBAVCODEC_VERSION_MICRO >= 100 -#define LIB_PREFIX "ffmpeg" -#else -#define LIB_PREFIX "libav" -#endif - -static bool print_prefix = true; - 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); - int type = extract_msg_type_from_ctx(ptr); - if (!mp_msg_test(type, mp_level)) + // Note: mp_log is thread-safe, but destruction of the log instances is not. + pthread_mutex_lock(&log_lock); + + if (!log_mpv_instance) { + pthread_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(log, mp_level)) { + if (log_print_prefix) + mp_msg_log(log, mp_level, "%s: ", avc ? avc->item_name(ptr) : "?"); + log_print_prefix = fmt[strlen(fmt) - 1] == '\n'; - if (print_prefix) { - mp_msg(type, mp_level, "[%s/%s] ", LIB_PREFIX, - avc ? avc->item_name(ptr) : "?"); + mp_msg_log_va(log, mp_level, fmt, vl); } - print_prefix = fmt[strlen(fmt) - 1] == '\n'; - mp_msg_va(type, mp_level, fmt, vl); + pthread_mutex_unlock(&log_lock); } -void init_libav(void) +void init_libav(struct mpv_global *global) { - av_log_set_callback(mp_msg_av_log_callback); + pthread_mutex_lock(&log_lock); + if (!log_mpv_instance) { + log_mpv_instance = global; + log_root = mp_log_new(NULL, global->log, LIB_PREFIX); + 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); + } + pthread_mutex_unlock(&log_lock); + avcodec_register_all(); av_register_all(); avformat_network_init(); @@ -146,6 +170,16 @@ void init_libav(void) #endif } +void uninit_libav(struct mpv_global *global) +{ + pthread_mutex_lock(&log_lock); + if (log_mpv_instance == global) { + log_mpv_instance = NULL; + talloc_free(log_root); + } + pthread_mutex_unlock(&log_lock); +} + #define V(x) (x)>>16, (x)>>8 & 255, (x) & 255 static void print_version(struct mp_log *log, int v, char *name, unsigned buildv, unsigned runv) diff --git a/common/av_log.h b/common/av_log.h index e941220a11..17326b609f 100644 --- a/common/av_log.h +++ b/common/av_log.h @@ -1,3 +1,8 @@ -void init_libav(void); +#ifndef MP_AV_LOG_H +#define MP_AV_LOG_H +struct mpv_global; struct mp_log; +void init_libav(struct mpv_global *global); +void uninit_libav(struct mpv_global *global); void print_libav_versions(struct mp_log *log, int v); +#endif diff --git a/player/main.c b/player/main.c index db2794c1ad..b1d5d8163e 100644 --- a/player/main.c +++ b/player/main.c @@ -140,6 +140,7 @@ static MP_NORETURN void exit_player(struct MPContext *mpctx, #endif getch2_disable(); + uninit_libav(mpctx->global); if (how != EXIT_NONE) { const char *reason; @@ -310,8 +311,7 @@ static int mpv_main(int argc, char *argv[]) struct MPOpts *opts = mpctx->opts; mpctx->global->opts = opts; - - init_libav(); + init_libav(mpctx->global); GetCpuCaps(&gCpuCaps); screenshot_init(mpctx); mpctx->mixer = mixer_init(mpctx, mpctx->global); -- cgit v1.2.3