剑客
关注科技互联网

FFMPEG Tips (1) 如何打印日志

由于如今的工作比以前忙了,已经有好些日子没有分享技术博文了,还是得继续坚持下去。鉴于如今视频直播如此火爆,那就选个主题,聊聊播放器、聊聊 FFMPEG 那些事吧。

FFMPEG 是个好东西,可以说目前市面上的任何一款“通用型”播放器,都离不开 FFMPEG,因为没有什么其他的库比它支持的格式更加全面了。

这里首先致敬一下雷神,博客地址: 《雷霄骅的专栏》 ,分享了很多音视频方面的技术文章、开源代码以及 FFMPEG 源码的分析,无论对入门者还是资深开发,都有很大的价值。

我要写的主题,与雷神不同,我会测重介绍使用 FFMPEG 开发播放器过程中的一些比较基础的小经验或者说开发笔记,因此,使用 Tips 这个单词,意味小技巧、小帖士,因此,本系列的目标读者是 FFMPEG 的入门者,也欢迎路过的高手们能对分享的内容给出宝贵的建议和意见。

本文则从开发和调试程序最重要的一点:打 LOG 说起,看看基于 FFMPEG 开发,如何打印 LOG,如何设置日志的级别。

1.  FFMPEG 打印日志的函数

FFMPEG 有一套自己的日志系统,它使用 av_log() 函数来打印日志,其声明位于:<libavutil/log.h>

它的函数原型如下:

/**
 * Send the specified message to the log if the level is less than or equal
 * to the current av_log_level. By default, all logging messages are sent to
 * stderr. This behavior can be altered by setting a different logging callback
 * function.
 * @see av_log_set_callback
 *
 * @param avcl A pointer to an arbitrary struct of which the first field is a
 *        pointer to an AVClass struct.
 * @param level The importance level of the message expressed using a @ref
 *        lavu_log_constants "Logging Constant".
 * @param fmt The format string (printf-compatible) that specifies how
 *        subsequent arguments are converted to output.
 */
void av_log(void *avcl, int level, const char *fmt, …);

参数含义:

avcl:指定一个包含 AVClass 的结构体,指定该 log 所属的结构体,如 AVFormatContext、AVCodecContext 等等,可以设置为 NULL

level:log 的级别,下面给出可选的值

fmt:跟 c 语言的 printf() 定义一样

2.  FFMPEG 日志级别

LOG 的级别是一个 int 类型,其可选的数值及其含义如下:

/**
 * Print no output.
 */
#define AV_LOG_QUIET    -8

/**
 * Something went really wrong and we will crash now.
 */
#define AV_LOG_PANIC     0

/**
 * Something went wrong and recovery is not possible.
 * For example, no header was found for a format which depends
 * on headers or an illegal combination of parameters is used.
 */
#define AV_LOG_FATAL     8

/**
 * Something went wrong and cannot losslessly be recovered.
 * However, not all future data is affected.
 */
#define AV_LOG_ERROR    16

/**
 * Something somehow does not look correct. This may or may not
 * lead to problems. An example would be the use of '-vstrict -2'.
 */
#define AV_LOG_WARNING  24

/**
 * Standard information.
 */
#define AV_LOG_INFO     32

/**
 * Detailed information.
 */
#define AV_LOG_VERBOSE  40

/**
 * Stuff which is only useful for libav* developers.
 */
#define AV_LOG_DEBUG    48

3.  FFMPEG 设置和获取当前日志级别

由一个全局的变量来控制哪个级别及以上的日志会打印输出,设置和获取这个全局变量的函数如下:

/**
 * Get the current log level
 *
 * @see lavu_log_constants
 *
 * @return Current log level
 */
int av_log_get_level(void);

/**
 * Set the log level
 *
 * @see lavu_log_constants
 *
 * @param level Logging level
 */
void av_log_set_level(int level);

例如,当全局的日志级别设置为 `AV_LOG_ERROR`,那么凡是日志级别高于 `AV_LOG_ERROR` 的日志,都不会被打印出来。

4.  FFMPEG 日志打印函数的使用示例

假设要打印 DEBUG 和 ERROR 级别的日志,用法示例如下:

av_log(NULL, AV_LOG_DEBUG, "Hello World ! /n");
av_log(NULL, AV_LOG_ERROR, "Error:%d ! /n", errorCode);

5.  FFMPEG 日志打印函数的封装

当然,如果你觉得 av_log 用起来不是很顺手,你可以定义个宏封装下,例如:

#ifndef _SYS_LOG_
#define _SYS_LOG_

#include <libavutil/log.h>

#define LOGD(format, ...) av_log(NULL, AV_LOG_DEBUG, format, ##__VA_ARGS__);
#define LOGV(format, ...) av_log(NULL, AV_LOG_VERBOSE, format, ##__VA_ARGS__);
#define LOGI(format, ...) av_log(NULL, AV_LOG_INFO, format, ##__VA_ARGS__);
#define LOGW(format, ...) av_log(NULL, AV_LOG_WARNING, format, ##__VA_ARGS__);
#define LOGE(format, ...) av_log(NULL, AV_LOG_ERROR, format, ##__VA_ARGS__);

#endif

6.  Android 中打印 FFMPEG 的日志

由于 FFMPEG 默认使用的是 printf 来打印日志,而 Android 系统有着一套自己的 LOG 系统,因此,需要让 FFMPEG 的日志重定向使用 Android 的日志系统,具体方法描述如下:

通过 FFMPEG 的 av_log_set_callback() 注册一个 LOG callback function,FFMPEG 就会把 LOG 打印功能重定向到 callback function 中,代码示例如下:

#ifdef __ANDROID_API__
#include <android/log.h>

#define ALOG(level, TAG, ...)    ((void)__android_log_vprint(level, TAG, __VA_ARGS__))

#define SYS_LOG_TAG "nmplayer"

static void syslog_print(void *ptr, int level, const char *fmt, va_list vl)
{
    switch(level) {
    case AV_LOG_DEBUG:
        ALOG(ANDROID_LOG_VERBOSE, SYS_LOG_TAG, fmt, vl);
        break;
    case AV_LOG_VERBOSE:
        ALOG(ANDROID_LOG_DEBUG, SYS_LOG_TAG, fmt, vl);
        break;
    case AV_LOG_INFO:
        ALOG(ANDROID_LOG_INFO, SYS_LOG_TAG, fmt, vl);
        break;
    case AV_LOG_WARNING:
        ALOG(ANDROID_LOG_WARN, SYS_LOG_TAG, fmt, vl);
        break;
    case AV_LOG_ERROR:
        ALOG(ANDROID_LOG_ERROR, SYS_LOG_TAG, fmt, vl);
        break;
    }
}

static void syslog_init()
{
    av_log_set_callback(syslog_print);
}

#endif // __ANDROID_API__

在代码初始化的地方调用一下 syslog_init() 后,就可以使用 av_log() 在 Android 平台输出调试日志了。

7.  FFPlay 设置日志级别

平时自己写的播放器播放某些流播放有问题的话,也可以使用 ffplay 来对比调试一下,看看使用 ffplay 是否可以播放,报错信息是什么,ffplay 打开 DEBUG 日志输出的方法示例如下:

$ ffplay -v debug $URL

-v 参数是用于配制 ffplay 的日志级别,其定义如下:

-loglevel [repeat+]loglevel | -v [repeat+]loglevel

Set the logging level used by the library. Adding "repeat+" indicates that repeated log output should not be compressed to the first line and the "Last message repeated n times" line will be omitted. "repeat" can also be used alone. If "repeat" is used alone, and with no prior loglevel set, the default loglevel will be used. If multiple loglevel parameters are given, using ’repeat’ will not change the loglevel. loglevel is a string or a number containing one of the following values:

‘quiet, -8’
Show nothing at all; be silent.

‘panic, 0’
Only show fatal errors which could lead the process to crash, such as an assertion failure. This is not currently used for anything.

‘fatal, 8’
Only show fatal errors. These are errors after which the process absolutely cannot continue.

‘error, 16’
Show all errors, including ones which can be recovered from.

‘warning, 24’
Show all warnings and errors. Any message related to possibly incorrect or unexpected events will be shown.

‘info, 32’
Show informative messages during processing. This is in addition to warnings and errors. This is the default value.

‘verbose, 40’
Same as info, except more verbose.

‘debug, 48’
Show everything, including debugging information.

‘trace, 56’
By default the program logs to stderr. If coloring is supported by the terminal, colors are used to mark errors and warnings. Log coloring can be disabled setting the environment variable AV_LOG_FORCE_NOCOLOR or NO_COLOR, or can be forced setting the environment variable AV_LOG_FORCE_COLOR. The use of the environment variable NO_COLOR is deprecated and will be dropped in a future FFmpeg version.

8.  小结

关于如何使用 FFMPEG 如何打印日志就介绍到这儿了,文章中有不清楚的地方欢迎留言或者来信 lujun.hust@gmail.com 交流,关注我的新浪微博@卢_俊或者 微信公众号 @Jhuster 获取最新的文章和资讯。

FFMPEG Tips (1) 如何打印日志

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址