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.
This commit is contained in:
wm4 2013-12-21 20:06:36 +01:00
parent 7bdee8f35e
commit d57eaa7e30
3 changed files with 74 additions and 35 deletions

View File

@ -21,11 +21,16 @@
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <pthread.h>
#include "av_log.h"
#include "config.h"
#include "common/common.h"
#include "common/global.h"
#include "common/msg.h"
#include <libavutil/avutil.h>
#include <libavutil/log.h>
@ -48,6 +53,19 @@
#include <libswresample/swresample.h>
#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;
if (print_prefix) {
mp_msg(type, mp_level, "[%s/%s] ", LIB_PREFIX,
avc ? avc->item_name(ptr) : "?");
}
print_prefix = fmt[strlen(fmt) - 1] == '\n';
mp_msg_va(type, mp_level, fmt, vl);
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';
mp_msg_log_va(log, 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)

View File

@ -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

View File

@ -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);