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


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


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


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


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


1. FFMPEG 打印日志的函数


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


它的函数原型如下:


/***Sendthespecifiedmessagetothelogifthelevelislessthanorequal*tothecurrentav_log_level.Bydefault,allloggingmessagesaresentto*stderr.Thisbehaviorcanbealteredbysettingadifferentloggingcallback*function.*@seeav_log_set_callback**@paramavclApointertoanarbitrarystructofwhichthefirstfieldisa*pointertoanAVClassstruct.*@paramlevelTheimportancelevelofthemessageexpressedusinga@ref*lavu_log_constants"LoggingConstant".*@paramfmtTheformatstring(printf-compatible)thatspecifieshow*subsequentargumentsareconvertedtooutput.*/voidav_log(void*avcl,intlevel,constchar*fmt,…);


参数含义:


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

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

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


2. FFMPEG 日志级别


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


/***Printnooutput.*/#defineAV_LOG_QUIET-8/***Somethingwentreallywrongandwewillcrashnow.*/#defineAV_LOG_PANIC0/***Somethingwentwrongandrecoveryisnotpossible.*Forexample,noheaderwasfoundforaformatwhichdepends*onheadersoranillegalcombinationofparametersisused.*/#defineAV_LOG_FATAL8/***Somethingwentwrongandcannotlosslesslyberecovered.*However,notallfuturedataisaffected.*/#defineAV_LOG_ERROR16/***Somethingsomehowdoesnotlookcorrect.Thismayormaynot*leadtoproblems.Anexamplewouldbetheuseof'-vstrict-2'.*/#defineAV_LOG_WARNING24/***Standardinformation.*/#defineAV_LOG_INFO32/***Detailedinformation.*/#defineAV_LOG_VERBOSE40/***Stuffwhichisonlyusefulforlibav*developers.*/#defineAV_LOG_DEBUG48


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


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


/***Getthecurrentloglevel**@seelavu_log_constants**@returnCurrentloglevel*/intav_log_get_level(void);/***Settheloglevel**@seelavu_log_constants**@paramlevelLogginglevel*/voidav_log_set_level(intlevel);


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


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


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


av_log(NULL,AV_LOG_DEBUG,"HelloWorld!\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>#defineLOGD(format,...)av_log(NULL,AV_LOG_DEBUG,format,##__VA_ARGS__);#defineLOGV(format,...)av_log(NULL,AV_LOG_VERBOSE,format,##__VA_ARGS__);#defineLOGI(format,...)av_log(NULL,AV_LOG_INFO,format,##__VA_ARGS__);#defineLOGW(format,...)av_log(NULL,AV_LOG_WARNING,format,##__VA_ARGS__);#defineLOGE(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 中,代码示例如下(你也可以到我的 Github 查看封装好的源代码: https://github.com/Jhuster/clib):


#ifdef__ANDROID_API__#include<android/log.h>#defineALOG(level,TAG,...)((void)__android_log_vprint(level,TAG,__VA_ARGS__))#defineSYS_LOG_TAG"nmplayer"staticvoidsyslog_print(void*ptr,intlevel,constchar*fmt,va_listvl){switch(level){caseAV_LOG_DEBUG:ALOG(ANDROID_LOG_VERBOSE,SYS_LOG_TAG,fmt,vl);break;caseAV_LOG_VERBOSE:ALOG(ANDROID_LOG_DEBUG,SYS_LOG_TAG,fmt,vl);break;caseAV_LOG_INFO:ALOG(ANDROID_LOG_INFO,SYS_LOG_TAG,fmt,vl);break;caseAV_LOG_WARNING:ALOG(ANDROID_LOG_WARN,SYS_LOG_TAG,fmt,vl);break;caseAV_LOG_ERROR:ALOG(ANDROID_LOG_ERROR,SYS_LOG_TAG,fmt,vl);break;}}staticvoidsyslog_init(){av_log_set_callback(syslog_print);}#endif//__ANDROID_API__


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


7. FFPlay 设置日志级别


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


$ffplay-vdebug$URL


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


-loglevel[repeat+]loglevel|-v[repeat+]loglevelSetthelogginglevelusedbythelibrary.Adding"repeat+"indicatesthatrepeatedlogoutputshouldnotbecompressedtothefirstlineandthe"Lastmessagerepeatedntimes"linewillbeomitted."repeat"canalsobeusedalone.If"repeat"isusedalone,andwithnopriorloglevelset,thedefaultloglevelwillbeused.Ifmultipleloglevelparametersaregiven,using’repeat’willnotchangetheloglevel.loglevelisastringoranumbercontainingoneofthefollowingvalues:‘quiet,-8’Shownothingatall;besilent.‘panic,0’Onlyshowfatalerrorswhichcouldleadtheprocesstocrash,suchasanassertionfailure.Thisisnotcurrentlyusedforanything.‘fatal,8’Onlyshowfatalerrors.Theseareerrorsafterwhichtheprocessabsolutelycannotcontinue.‘error,16’Showallerrors,includingoneswhichcanberecoveredfrom.‘warning,24’Showallwarningsanderrors.Anymessagerelatedtopossiblyincorrectorunexpectedeventswillbeshown.‘info,32’Showinformativemessagesduringprocessing.Thisisinadditiontowarningsanderrors.Thisisthedefaultvalue.‘verbose,40’Sameasinfo,exceptmoreverbose.‘debug,48’Showeverything,includingdebugginginformation.‘trace,56’Bydefaulttheprogramlogstostderr.Ifcoloringissupportedbytheterminal,colorsareusedtomarkerrorsandwarnings.LogcoloringcanbedisabledsettingtheenvironmentvariableAV_LOG_FORCE_NOCOLORorNO_COLOR,orcanbeforcedsettingtheenvironmentvariableAV_LOG_FORCE_COLOR.TheuseoftheenvironmentvariableNO_COLORisdeprecatedandwillbedroppedinafutureFFmpegversion.


8. 小结


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