FFmpeg source code analysis: log log system

FFmpeg encapsulates a proprietary log log system, supports setting log level log level, and also supports log callback log callback, which is convenient for developers to debug and troubleshoot problems.

1. Log level

The log log is located in the libavutil module, and the log level declaration is located in log.h:

// 静默模式,不打印日志
#define AV_LOG_QUIET    -8

// 立即崩溃,退出程序
#define AV_LOG_PANIC     0

// 严重出错,无法修复
#define AV_LOG_FATAL     8

// 程序出错
#define AV_LOG_ERROR    16

// 警告
#define AV_LOG_WARNING  24

// 信息
#define AV_LOG_INFO     32

// 详细信息
#define AV_LOG_VERBOSE  40

// 调试日志
#define AV_LOG_DEBUG    48

// 跟踪日志
#define AV_LOG_TRACE    56

It can be seen that the interval of each log level is 8, and the higher the level, the smaller the value. The method set_log_level() to set the log level is located in log.c, where av_log_level is a static global variable, as follows:

static int av_log_level = AV_LOG_INFO;

void av_log_set_level(int level)
{
    av_log_level = level;
}

2. Log printing

The commonly used printing log method av_log(), the declaration is located in log.h:

/** 
 * 发送特定消息到小于等于当前等级的日志,默认全部发送到stderr
 *
 * @param avcl  指向任意结构体的指针,结构体第一个变量为AVClass或NULL
 * @param level 日志等级
 * @param fmt   字符串格式
 */
void av_log(void *avcl, int level, const char *fmt, ...) av_printf_format(3, 4);

 The implementation of the av_log() method is as follows. Before printing, call va_start to start, then print the log, and finally call va_end to end:

void av_log(void* avcl, int level, const char *fmt, ...)
{
    va_list vl;
    va_start(vl, fmt);
    av_vlog(avcl, level, fmt, vl);
    va_end(vl);
}

av_log() calls the av_vlog() method internally, converts the parameter avc1 to the AVClass pointer, assigns av_log_callback to the function pointer log_callback, calculates the log level, and finally calls back the log if log_callback is not empty:

void av_vlog(void* avcl, int level, const char *fmt, va_list vl)
{
    AVClass* avc = avcl ? *(AVClass **) avcl : NULL;
    void (*log_callback)(void*, int, const char*, va_list) = av_log_callback;
    if (avc && avc->version >= (50 << 16 | 15 << 8 | 2) &&
        avc->log_level_offset_offset && level >= AV_LOG_FATAL)
        level += *(int *) (((uint8_t *) avcl) + avc->log_level_offset_offset);
    if (log_callback)
        log_callback(avcl, level, fmt, vl);
}

3. Log callback

The method to set the log callback is set_log_level(), the code is as follows:

void av_log_set_callback(void (*callback)(void*, int, const char*, va_list))
{
    av_log_callback = callback;
}

av_log_callback has a default callback function:

void av_log_default_callback(void* ptr, int level, const char* fmt, va_list vl)
{
    static int print_prefix = 1;
    static int count;
    static char prev[LINE_SZ];
    AVBPrint part[4];
    char line[LINE_SZ];
    static int is_atty;
    int type[2];
    unsigned tint = 0;

    if (level >= 0) {
        tint = level & 0xff00;
        level &= 0xff;
    }

    if (level > av_log_level)
        return;
    ff_mutex_lock(&mutex);

    format_line(ptr, level, fmt, vl, part, &print_prefix, type);
    snprintf(line, sizeof(line), "%s%s%s%s", part[0].str, part[1].str, part[2].str, part[3].str);

#if HAVE_ISATTY
    if (!is_atty)
        is_atty = isatty(2) ? 1 : -1;
#endif

    if (print_prefix && (flags & AV_LOG_SKIP_REPEATED) && !strcmp(line, prev) &&
        *line && line[strlen(line) - 1] != '\r'){
        count++;
        if (is_atty == 1)
            fprintf(stderr, "    Last message repeated %d times\r", count);
        goto end;
    }
    if (count > 0) {
        fprintf(stderr, "    Last message repeated %d times\n", count);
        count = 0;
    }
    strcpy(prev, line);
    sanitize(part[0].str);
    colored_fputs(type[0], 0, part[0].str);
    sanitize(part[1].str);
    colored_fputs(type[1], 0, part[1].str);
    sanitize(part[2].str);
    colored_fputs(av_clip(level >> 3, 0, NB_LEVELS - 1), tint >> 8, part[2].str);
    sanitize(part[3].str);
    colored_fputs(av_clip(level >> 3, 0, NB_LEVELS - 1), tint >> 8, part[3].str);

#if CONFIG_VALGRIND_BACKTRACE
    if (level <= BACKTRACE_LOGLEVEL)
        VALGRIND_PRINTF_BACKTRACE("%s", "");
#endif
end:
    av_bprint_finalize(part+3, NULL);
    ff_mutex_unlock(&mutex);
}

static void (*av_log_callback)(void*, int, const char*, va_list) =
    av_log_default_callback;

Take a look at the level and callback set by the Android platform:

void ffmpeg_init() {
    av_log_set_level(AV_LOG_INFO);
    av_log_set_callback(log_callback);
}

After log_callback() receives the callback message, it can print the corresponding msg according to the level:

void log_callback(void *ptr, int level, const char *format, va_list args) {
    switch (level) {
        case AV_LOG_DEBUG:
            ALOGD(FFMPEG_TAG, format, args);
            break;
        case AV_LOG_INFO:
            ALOGI(FFMPEG_TAG, format, args);
            break;
        case AV_LOG_ERROR:
            ALOGE(FFMPEG_TAG, format, args);
            break;
        default:
            break;
    }
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324364277&siteId=291194637