Ffmpeg developers' guide -----------------by Alan Wang 1,Debug 在ffmpeg的源码上进行调试自然免不了打log,在ffmpeg中打log可以使用Ffmpeg中的API /** * 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 av_vlog callback * function. * * @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, lower values signifying * higher importance. * @param fmt The format string (printf-compatible) that specifies how * subsequent arguments are converted to output. * @see av_vlog */ //注意默认情况下,所有的logging msg都被输出到stderr上。 void av_log(void *avcl, int level, const char *fmt, ...) av_printf_format(3, 4); Ffmpeg中logging system的调用关系主要如下图: 其中这三个函数的原型如下: 1,void av_log(void *avcl, int level, const char *fmt, ...) av_printf_format(3, 4); //av_printf_format(3,4) ??? 2,void av_vlog(void *avcl, int level, const char *fmt, va_list); //注意va_list是一个依赖编译器实现的c宏,va_list代表一个指向参数列表的指针(char *) 3,void av_log_default_callback(void* ptr, int level, const char* fmt, va_list vl); 默认的情况下,av_vlog函数会调用默认的回调函数 av_log_default_callback来完成log的输出,其实这个函数内部调用的就是fprintf(stderr,....),所以默认的log都会输出到stderr,通过API void av_log_set_callback(void (*)(void*, int, const char*, va_list)); 来设置一个新的回调函数指针例如my_logging_callback,那么在这个函数的实现中可以自定义把函数输出到其他地方入stdout。my_logging_callback的实现自然可以用fprintf。 Ffmpeg强烈推荐使用av_log API来Debug,但是这样确实带来的不便性,开发者自然会想到使用printf()来打log调试。但是Ffmpeg做了一定的限制,有时候使用printf()会带来错误信息。原因是Ffmpeg在libavutil/internal.h文件中将 printf ,fprintf, malloc, free,exit....等等这些函数都重新define过了事例如下: #undef printf #define printf please_use_av_log_instead_of_printf #undef fprintf #define fprintf please_use_av_log_instead_of_fprintf #undef puts #define puts please_use_av_log_instead_of_puts 所以如果向使用printf函数,需要在使用前重新 #undef一下。 #undef printf printf(“Msg you want to sent to stdout”); 需要补充的是如果 ffplay.c ffmpeg.c ...这些测试程序中之间用printf来打log是没问题的,但是在这些程序中打log没有太大意义。Debug ffmpeg主要目的是取debug libavformat,libavcodec...这些库,在这些库中用printf会得到下面的编译阶段错误信息: libavformat/sbgdec.c:347: error: implicit declaration of function ‘please_use_av_log_instead_of_printf’ libavformat/sbgdec.c:348: warning: ISO C90 forbids mixed declarations and code make: *** [libavformat/sbgdec.o] Error 1 这里补充一个知识点: //下面是av_log的函数原型,后面的av_printf_format(3, 4)实际上是一个宏。 void av_log(void *avcl, int level, const char *fmt, ...) av_printf_format(3, 4); //av_printf_format(3,4) ??? //该宏定义的文件位于 libavutil/attributes.h av_printf_format(3,4) 这个宏展开后的结果是 :__attribute__((__format__(__printf__, fmtpos, attrpos))) #ifdef __GNUC__ # define av_builtin_constant_p __builtin_constant_p # define av_printf_format(fmtpos, attrpos) __attribute__((__format__(__printf__, fmtpos, attrpos))) #else # define av_builtin_constant_p(x) 0 # define av_printf_format(fmtpos, attrpos) #endif 可以看到这个__attribute__属性是一个gun c编译器的一个特色,这个attribute告诉编译器对这个av_log函数进行format检查,怎么检查?按照 printf函数的格式化方式进行参数检查。其中fmtpos(3) 代表需要格式化的字符串在av_log函数中的参数的位置是第3个,attrpos(4)表示第一个被格式化的变量值(可能是%s,%d)出现在av_log函数中的参数的位置是第4个。Gnu c的__attribute__作用是可以告诉编译器,让编译器做进一步的检查。 那么av_log的函数声明就可以被解释了。 Void av_log(void *avcl, int level, const char *fmt, …) __attribute__((_format_(_printf_, 3, 4)));