您好,登錄后才能下訂單哦!
由于如今的工作比以前忙了,已經(jīng)有好些日子沒有分享技術(shù)博文了,還是得繼續(xù)堅持下去。鑒于如今視頻直播如此火爆,那就選個主題,聊聊播放器、聊聊 FFMPEG 那些事吧。
FFMPEG 是個好東西,可以說目前市面上的任何一款“通用型”播放器,都離不開 FFMPEG,因為沒有什么其他的庫比它支持的格式更加全面了。
這里首先致敬一下雷神,博客地址:《雷霄驊的專欄》,分享了很多音視頻方面的技術(shù)文章、開源代碼以及 FFMPEG 源碼的分析,無論對入門者還是資深開發(fā),都有很大的價值。
我要寫的主題,與雷神不同,我會測重介紹使用 FFMPEG 開發(fā)播放器過程中的一些比較基礎(chǔ)的小經(jīng)驗或者說開發(fā)筆記,因此,使用 Tips 這個單詞,意味小技巧、小帖士,因此,本系列的目標(biāo)讀者是 FFMPEG 的入門者,也歡迎路過的高手們能對分享的內(nèi)容給出寶貴的建議和意見。
本文則從開發(fā)和調(diào)試程序最重要的一點:打 LOG 說起,看看基于 FFMPEG 開發(fā),如何打印 LOG,如何設(shè)置日志的級別。
1. FFMPEG 打印日志的函數(shù)
FFMPEG 有一套自己的日志系統(tǒng),它使用 av_log() 函數(shù)來打印日志,其聲明位于:<libavutil/log.h>
它的函數(shù)原型如下:
/** * 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, …);
參數(shù)含義:
avcl:指定一個包含 AVClass 的結(jié)構(gòu)體,指定該 log 所屬的結(jié)構(gòu)體,如 AVFormatContext、AVCodecContext 等等,可以設(shè)置為 NULL
level:log 的級別,下面給出可選的值
fmt:跟 c 語言的 printf() 定義一樣
2. FFMPEG 日志級別
LOG 的級別是一個 int 類型,其可選的數(shù)值及其含義如下:
/** * 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 設(shè)置和獲取當(dāng)前日志級別
由一個全局的變量來控制哪個級別及以上的日志會打印輸出,設(shè)置和獲取這個全局變量的函數(shù)如下:
/** * 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);
例如,當(dāng)全局的日志級別設(shè)置為 `AV_LOG_ERROR`,那么凡是日志級別高于 `AV_LOG_ERROR` 的日志,都不會被打印出來。
4. FFMPEG 日志打印函數(shù)的使用示例
假設(shè)要打印 DEBUG 和 ERROR 級別的日志,用法示例如下:
av_log(NULL, AV_LOG_DEBUG, "Hello World ! \n"); av_log(NULL, AV_LOG_ERROR, "Error:%d ! \n", errorCode);
5. FFMPEG 日志打印函數(shù)的封裝
當(dāng)然,如果你覺得 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 默認(rèn)使用的是 printf 來打印日志,而 Android 系統(tǒng)有著一套自己的 LOG 系統(tǒng),因此,需要讓 FFMPEG 的日志重定向使用 Android 的日志系統(tǒng),具體方法描述如下:
通過 FFMPEG 的 av_log_set_callback() 注冊一個 LOG callback function,F(xiàn)FMPEG 就會把 LOG 打印功能重定向到 callback function 中,代碼示例如下(你也可以到我的 Github 查看封裝好的源代碼: https://github.com/Jhuster/clib):
#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__
在代碼初始化的地方調(diào)用一下 syslog_init() 后,就可以使用 av_log() 在 Android 平臺輸出調(diào)試日志了。
7. FFPlay 設(shè)置日志級別
平時自己寫的播放器播放某些流播放有問題的話,也可以使用 ffplay 來對比調(diào)試一下,看看使用 ffplay 是否可以播放,報錯信息是什么,ffplay 打開 DEBUG 日志輸出的方法示例如下:
$ ffplay -v debug $URL
-v 參數(shù)是用于配制 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. 小結(jié)
關(guān)于如何使用 FFMPEG 如何打印日志就介紹到這兒了,文章中有不清楚的地方歡迎留言或者來信 lujun.hust@gmail.com 交流,關(guān)注我的新浪微博 @盧_俊 或者 微信公眾號 @Jhuster 獲取最新的文章和資訊。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。