我下面介绍的使用CMake的方式编译native的,直接使用Android.mk也可以。
1.建立一个support c++的Android工程
2.MainActivity.java复制过去,把res复制过去(之后可能会报一些xml形式的drawable找不到某些图片,这时可以随便复制命名一张图片放到drawable上就好了),在main下面建立一个叫jniLibs的文件夹,把so文件复制到jniLibs,结构如下图。把c、cpp源文件放到main/cpp下。把头文件放到main/cpp/include下。
4.配置app/build.gradle文件。主要看配置CMakeList和abiFilters
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
defaultConfig {
applicationId "com.example.champion.ffmpegtranscode"
minSdkVersion 24
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags ""
}
ndk {
// abiFiliter: ABI 过滤器(application binary interface,应用二进制接口)
// Android 支持的 CPU 架构
abiFilters 'armeabi'//,'armeabi-v7a','arm64-v8a','x86','x86_64','mips','mips64'
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.1'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
5.配置CMakeList.txt,上面已经指明路径了,就是在app/CMakeList.txt
主要编译无源动态库(so为src)和有源动态库(c,cpp文件为src)。最后要把log-lib和所有ffmpeg标准so和自己编写代码的那个so使用target_link_libraries链接到一起。在该文件中指明头文件的目录,使用的是include_directories( src/main/cpp/include )。可以直接复制该文件覆盖原来的,然后修改自己编译的so库的名字和编译所需的源文件。
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp )
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib} )
add_library(
sfftranscoder
SHARED
src/main/cpp/cmdutils.c
src/main/cpp/ffmpeg_filter.c
src/main/cpp/ffmpeg_jni.c
src/main/cpp/ffmpeg_mod.c
src/main/cpp/ffmpeg_opt.c
)
add_library(
avcodec-56
SHARED
IMPORTED )
set_target_properties(avcodec-56
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavcodec-56.so)
add_library(
avdevice-56
SHARED
IMPORTED )
set_target_properties(avdevice-56
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavdevice-56.so)
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
#target_link_libraries(
# avdevice-56
# ${log-lib} )
add_library(
avfilter-5
SHARED
IMPORTED )
set_target_properties(avfilter-5
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavfilter-5.so)
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
#target_link_libraries(
# avfilter-5
# ${log-lib} )
add_library(
avformat-56
SHARED
IMPORTED )
set_target_properties(avformat-56
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavformat-56.so)
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
#target_link_libraries(
# avformat-56
# ${log-lib} )
add_library(
avutil-54
SHARED
IMPORTED )
set_target_properties(avutil-54
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavutil-54.so)
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
#target_link_libraries(
# avutil-54
# ${log-lib} )
add_library(
postproc-53
SHARED
IMPORTED )
set_target_properties(postproc-53
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libpostproc-53.so)
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
#target_link_libraries(
# postproc-53
# ${log-lib} )
add_library(
swresample-1
SHARED
IMPORTED )
set_target_properties(swresample-1
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libswresample-1.so)
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
#target_link_libraries(
# swresample-1
# ${log-lib} )
add_library(
swscale-3
SHARED
IMPORTED )
set_target_properties(swscale-3
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libswscale-3.so)
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
#target_link_libraries(
# swscale-3
# ${log-lib} )
target_link_libraries(sfftranscoder
avcodec-56
avdevice-56
avfilter-5
avformat-56
avutil-54
postproc-53
swresample-1
swscale-3
${log-lib} )
include_directories( src/main/cpp/include )
6.修改jni文件中的jni方法的名字,与声明native方法所在的包名+类名一致的名字。
7.可能有些头文件需要增加声明,或者修改头文件的路径,如#include "libavutil/dict.h"改成#include "libxxxx/dict.h"
8.如果有些资源的错误会导致R编译不出来就会缺少R文件,如果R文件有了,还是不行,就是声明一下R。app_packagename.R
9.在Manifest中声明读写权限,在代码中动态申请读写,加上一个回调方法。如果不动态申请,那么在native中打开文件也会permission denied
checkSelfPermission("android.permission.WRITE_EXTERNAL_STORAGE");
shouldShowRequestPermissionRationale("android.permission.WRITE_EXTERNAL_STORAGE");
requestPermissions(new String[]{"android.permission.WRITE_EXTERNAL_STORAGE"}, 10);
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions,int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
Log.e(TAG, "requestCode: " + requestCode + " permissions: " + permissions.toString()
+ "grantResults: " + grantResults.toString());
}
调试,如果执行程序不顺利,logcat > 重定向到一个文件中,可以看到下面的native的log:
06-12 20:46:16.549 30100 30100 F libc : Fatal signal 11 (SIGSEGV), code 1, fault addr 0x18 in tid 30100 (ffmpegtranscode)
06-12 20:46:16.629 30169 30169 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
06-12 20:46:16.629 30169 30169 F DEBUG : Build fingerprint: 'honor/FRD-AL10/HWFRD:7.0/HUAWEIFRD-AL10/C00B391:user/release-keys'
06-12 20:46:16.629 30169 30169 F DEBUG : Revision: '0'
06-12 20:46:16.629 30169 30169 F DEBUG : ABI: 'arm'
06-12 20:46:16.630 30169 30169 F DEBUG : pid: 30100, tid: 30100, name: ffmpegtranscode >>> com.example.champion.ffmpegtranscode <<<
06-12 20:46:16.630 30169 30169 F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x18
06-12 20:46:16.630 30169 30169 F DEBUG : r0 00000000 r1 00000000 r2 de0cbcb3 r3 00000000
06-12 20:46:16.630 30169 30169 F DEBUG : r4 d367f8d9 r5 c62db08c r6 d367f8d9 r7 ffd7d890
06-12 20:46:16.630 30169 30169 F DEBUG : r8 ffd7ddc8 r9 e5404f00 sl ffd7dcd0 fp e5404f00
06-12 20:46:16.630 30169 30169 F DEBUG : ip de0d5bd0 sp ffd7d6f0 lr de0b1f8b pc de0c04ca cpsr 600f0030
06-12 20:46:16.643 30169 30169 F DEBUG :
06-12 20:46:16.643 30169 30169 F DEBUG : backtrace:
06-12 20:46:16.651 30169 30169 F DEBUG : #00 pc 0001f4ca /data/app/com.example.champion.ffmpegtranscode-1/lib/arm/libsfftranscoder.so
06-12 20:46:16.651 30169 30169 F DEBUG : #01 pc 0001ee37 /data/app/com.example.champion.ffmpegtranscode-1/lib/arm/libsfftranscoder.so
06-12 20:46:16.651 30169 30169 F DEBUG : #02 pc 0001ec93 /data/app/com.example.champion.ffmpegtranscode-1/lib/arm/libsfftranscoder.so (ffmpeg_parse_options+158)
06-12 20:46:16.651 30169 30169 F DEBUG : #03 pc 0001116f /data/app/com.example.champion.ffmpegtranscode-1/lib/arm/libsfftranscoder.so (ffmpegmain+178)
06-12 20:46:16.651 30169 30169 F DEBUG : #04 pc 00010cb1 /data/app/com.example.champion.ffmpegtranscode-1/lib/arm/libsfftranscoder.so (Java_com_example_champion_ffmpegtranscode_MainActivity_ffmpegcore+184)
06-12 20:46:16.651 30169 30169 F DEBUG : #05 pc 000adf19 /system/lib/libart.so (art_quick_generic_jni_trampoline+40)
06-12 20:46:16.651 30169 30169 F DEBUG : #06 pc 000a9541 /system/lib/libart.so (art_quick_invoke_stub_internal+64)
06-12 20:46:16.651 30169 30169 F DEBUG : #07 pc 00406c6d /system/lib/libart.so (art_quick_invoke_stub+232)
06-12 20:46:16.651 30169 30169 F DEBUG : #08 pc 000b0809 /system/lib/libart.so (_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+136)
06-12 20:46:16.651 30169 30169 F DEBUG : #09 pc 001eca3f /system/lib/libart.so (_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPKNS_7DexFile8CodeItemEPNS_11ShadowFrameEPNS_6JValueE+198)
06-12 20:46:16.651 30169 30169 F DEBUG : #10 pc 001e6fdf /system/lib/libart.so (_ZN3art11interpreter6DoCallILb0ELb0EEEbPNS_9ArtMethodEPNS_6ThreadERNS_11ShadowFrameEPKNS_11InstructionEtPNS_6JValueE+478)
06-12 20:46:16.651 30169 30169 F DEBUG : #11 pc 003fe63f /system/lib/libart.so (MterpInvokeVirtual+422)
06-12 20:46:16.651 30169 30169 F DEBUG : #12 pc 0009c394 /system/lib/libart.so (ExecuteMterpImpl+14228)
06-12 20:46:16.652 30169 30169 F DEBUG : #13 pc 001cad4b /system/lib/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadEPKNS_7DexFile8CodeItemERNS_11ShadowFrameENS_6JValueEb+290)
06-12 20:46:16.652 30169 30169 F DEBUG : #14 pc 001cf815 /system/lib/libart.so (_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadEPKNS_7DexFile8CodeItemEPNS_11ShadowFrameE+92)
06-12 20:46:16.652 30169 30169 F DEBUG : #15 pc 003f611d /system/lib/libart.so (artQuickToInterpreterBridge+716)
06-12 20:46:16.652 30169 30169 F DEBUG : #16 pc 000adf93 /system/lib/libart.so (art_quick_to_interpreter_bridge+34)
06-12 20:46:16.652 30169 30169 F DEBUG : #17 pc 74f1a40f /data/dalvik-cache/arm/system@[email protected] (offset 0x17b1000)
06-12 20:47:21.716 30465 30465 F libc : Fatal signal 11 (SIGSEGV), code 1, fault addr 0x18 in tid 30465 (ffmpegtranscode)
可以看到#02 pc 0001ec93 /data/app/com.example.champion.ffmpegtranscode-1/lib/arm/libsfftranscoder.so (ffmpeg_parse_options+158)
进入了ffmpegmain,然后再进入ffmpeg_parse_options方法,那么就打log调试究竟具体是出在哪个一句代码,在每个有点可疑的语句前后加log,最笨最有效的方法。
在jni和native中打log:
#include
#include
#define LOGE(FORMAT, ...) __android_log_print(ANDROID_LOG_ERROR,"LC",FORMAT,##__VA_ARGS__);
在代码中就是使用了: LOGE("注册成功")
搜索LC就可以过滤出所有由这个宏打出来的log,注意LC是上面定义的,可以更改的。
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:
case AV_LOG_FATAL:
ALOG(ANDROID_LOG_ERROR, SYS_LOG_TAG, fmt, vl);
break;
}
}
static void syslog_init()
{
av_log_set_callback(syslog_print);
}
使用:
syslog_init();
// av_log_set_callback(custom_log);
av_log(NULL, AV_LOG_ERROR, "ffmpegmain_log: ");
这样就可以直接向Android Log一样输出。
也可以把他输出到一个文件中:
定义一个回调(注意:如果该so库的其他文件中定义了这个custom_log方法那么就不能定义了否则会编译报错,只需在使用的位置前面声明就好了):
//Output FFmpeg's av_log()
void custom_log(void *ptr, int level, const char* fmt, va_list vl){
//To TXT file
FILE *fp=fopen("/storage/emulated/0/av_log.txt","a+");
if(fp){
vfprintf(fp,fmt,vl);
fflush(fp);
fclose(fp);
}
//To Logcat
//LOGE(fmt, vl);
}
设置一个回调:av_log_set_callback(custom_log);
使用,和之前的方法一样的:av_log(NULL, AV_LOG_ERROR, "ffmpegmain_log: ");
关于ffmpeg的log输出系统:
----------------------------------------------------------------------------------------------------------------------
转载自:http://blog.51cto.com/ticktick/1867059
由于如今的工作比以前忙了,已经有好些日子没有分享技术博文了,还是得继续坚持下去。鉴于如今视频直播如此火爆,那就选个主题,聊聊播放器、聊聊 FFMPEG 那些事吧。
FFMPEG 是个好东西,可以说目前市面上的任何一款“通用型”播放器,都离不开 FFMPEG,因为没有什么其他的库比它支持的格式更加全面了。
这里首先致敬一下雷神,博客地址:《雷霄骅的专栏》,分享了很多音视频方面的技术文章、开源代码以及 FFMPEG 源码的分析,无论对入门者还是资深开发,都有很大的价值。
我要写的主题,与雷神不同,我会测重介绍使用 FFMPEG 开发播放器过程中的一些比较基础的小经验或者说开发笔记,因此,使用 Tips 这个单词,意味小技巧、小帖士,因此,本系列的目标读者是 FFMPEG 的入门者,也欢迎路过的高手们能对分享的内容给出宝贵的建议和意见。
本文则从开发和调试程序最重要的一点:打 LOG 说起,看看基于 FFMPEG 开发,如何打印 LOG,如何设置日志的级别。
1. FFMPEG 打印日志的函数
FFMPEG 有一套自己的日志系统,它使用 av_log() 函数来打印日志,其声明位于:
它的函数原型如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
/**
* 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 类型,其可选的数值及其含义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
/**
* 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 设置和获取当前日志级别
由一个全局的变量来控制哪个级别及以上的日志会打印输出,设置和获取这个全局变量的函数如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/**
* 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 级别的日志,用法示例如下:
1
2
|
av_log(NULL, AV_LOG_DEBUG,
"Hello World ! \n"
);
av_log(NULL, AV_LOG_ERROR,
"Error:%d ! \n"
, errorCode);
|
5. FFMPEG 日志打印函数的封装
当然,如果你觉得 av_log 用起来不是很顺手,你可以定义个宏封装下,例如:
1
2
3
4
5
6
7
8
9
10
11
12
|
#ifndef _SYS_LOG_
#define _SYS_LOG_
#include
#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 中,代码示例如下(你也可以到我的 Github 查看封装好的源代码: https://github.com/Jhuster/clib):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
#ifdef __ANDROID_API__
#include
#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 日志输出的方法示例如下:
1
|
$ ffplay -v debug $URL
|
-v 参数是用于配制 ffplay 的日志级别,其定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
-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 ar
|
-----------------------------------------------------------------------------------------------------------
转自:https://blog.csdn.net/leixiaohua1020/article/details/44243155
FFmpeg的库函数源代码分析文章列表:
【架构图】
FFmpeg源代码结构图 - 解码
FFmpeg源代码结构图 - 编码
【通用】
FFmpeg 源代码简单分析:av_register_all()
FFmpeg 源代码简单分析:avcodec_register_all()
FFmpeg 源代码简单分析:内存的分配和释放(av_malloc()、av_free()等)
FFmpeg 源代码简单分析:常见结构体的初始化和销毁(AVFormatContext,AVFrame等)
FFmpeg 源代码简单分析:avio_open2()
FFmpeg 源代码简单分析:av_find_decoder()和av_find_encoder()
FFmpeg 源代码简单分析:avcodec_open2()
FFmpeg 源代码简单分析:avcodec_close()
【解码】
图解FFMPEG打开媒体的函数avformat_open_input
FFmpeg 源代码简单分析:avformat_open_input()
FFmpeg 源代码简单分析:avformat_find_stream_info()
FFmpeg 源代码简单分析:av_read_frame()
FFmpeg 源代码简单分析:avcodec_decode_video2()
FFmpeg 源代码简单分析:avformat_close_input()
【编码】
FFmpeg 源代码简单分析:avformat_alloc_output_context2()
FFmpeg 源代码简单分析:avformat_write_header()
FFmpeg 源代码简单分析:avcodec_encode_video()
FFmpeg 源代码简单分析:av_write_frame()
FFmpeg 源代码简单分析:av_write_trailer()
【其它】
FFmpeg源代码简单分析:日志输出系统(av_log()等)
FFmpeg源代码简单分析:结构体成员管理系统-AVClass
FFmpeg源代码简单分析:结构体成员管理系统-AVOption
FFmpeg源代码简单分析:libswscale的sws_getContext()
FFmpeg源代码简单分析:libswscale的sws_scale()
FFmpeg源代码简单分析:libavdevice的avdevice_register_all()
FFmpeg源代码简单分析:libavdevice的gdigrab
【脚本】
FFmpeg源代码简单分析:makefile
FFmpeg源代码简单分析:configure
【H.264】
FFmpeg的H.264解码器源代码简单分析:概述
=====================================================
本文分析一下FFmpeg的日志(Log)输出系统的源代码。日志输出部分的核心函数只有一个:av_log()。使用av_log()在控制台输出日志的效果如下图所示。
FFmpeg日志输出系统的函数调用结构图如图所示。
这个函数的声明有两个地方比较特殊:
(1)函数最后一个参数是“…”。后文中对此再作详细分析。
(2)它的声明后面有一个av_printf_format(3, 4)。有关这个地方的左右还没有深入研究,网上资料中说它的作用是按照printf()的格式检查av_log()的格式。av_log()每个字段的含义如下:由此可见,av_log()和printf()的不同主要在于前面多了两个参数。其中第一个参数指定该log所属的结构体,例如AVFormatContext、AVCodecContext等等。第二个参数指定log的级别,源代码中定义了如下几个级别。
avcl:指定一个包含AVClass的结构体。
level:log的级别
fmt:和printf()一样。
从定义中可以看出来,随着严重程度逐渐下降,一共包含如下级别:AV_LOG_PANIC,AV_LOG_FATAL,AV_LOG_ERROR,AV_LOG_WARNING,AV_LOG_INFO,AV_LOG_VERBOSE,AV_LOG_DEBUG。每个级别定义的数值代表了严重程度,数值越小代表越严重。默认的级别是AV_LOG_INFO。此外,还有一个级别不输出任何信息,即AV_LOG_QUIET。
当前系统存在着一个“Log级别”。所有严重程度高于该级别的Log信息都会输出出来。例如当前的Log级别是AV_LOG_WARNING,则会输出AV_LOG_PANIC,AV_LOG_FATAL,AV_LOG_ERROR,AV_LOG_WARNING级别的信息,而不会输出AV_LOG_INFO级别的信息。可以通过av_log_get_level()获得当前Log的级别,通过另一个函数av_log_set_level()设置当前的Log级别。
av_log_get_level()的定义如下所示
可以通过av_log_set_level()设置当前Log的级别。
(1)va_list变量va_list是一个指向函数的参数的指针。va_start()用于初始化va_list变量。va_arg()用于返回可变参数。va_start()用于结束可变参数的获取。有关它们的用法可以参考一个小demo,如下所示。
(2)va_start()
(3)va_arg()
(4)va_end()
从声明中可以看出,av_vlog()和av_log()的参数基本上是一模一样的。唯一的不同在于av_log()中的“…”变成了av_vlog()中的va_list。
av_vlog()的定义位于libavutil\log.c中,如下所示。(1)如果输入参数level大于系统当前的日志级别av_log_level,表明不需要做任何处理,直接返回。
(2)调用format_line()设定Log的输出格式。
(3)调用colored_fputs()设定Log的颜色。
看完以上几个与AVBPrint相关函数之后,就可以来看一下format_line()的代码了。例如,part[0]对应的是目标结构体的父结构体的名称(如果父结构体存在的话);其打印格式形如“[%s @ %p]”,其中前面的“%s”对应父结构体的名称,“%p”对应其所在的地址。part[1]对应的是目标结构体的名称;其打印格式形如“[%s @ %p]”,其中前面的“%s”对应本结构体的名称,“%p”对应其所在的地址。part[2]用于输出Log的级别,这个字符串只有在flag中设置AV_LOG_PRINT_LEVEL的时候才能打印。part[3]则是打印原本传送进来的文本。将format_line()函数处理后得到的4个字符串连接其来,就可以的到一条完整的Log信息。下面图显示了flag设置AV_LOG_PRINT_LEVEL后的打印出来的Log的格式。
colored_fputs()函数用于将输出的文本“上色”并且输出。在这里有一点需要注意:Windows和Linux下控制台程序上色的方法是不一样的。Windows下是通过SetConsoleTextAttribute()方法给控制台中的文本上色;Linux下则是通过添加一些ANSI控制码完成上色。
Linux下控制台颜色是通过添加专用数字来选择的。这些数字夹在 "\e["和 "m"之间。如果指定一个以上的数字,则用分号将它们分开。
举几个例子:
(1)第一个数字(31)为前景颜色(红色);第二个数字为(42)背景颜色(绿色)
hConsoleOutput:指向控制台的句柄。
wAttributes:文本属性。
STD_INPUT_HANDLE: 标准输入的句柄wAttributes可以控制前景色和背景色:
STD_OUTPUT_HANDLE: 标准输出的句柄
STD_ERROR_HANDLE: 标准错误的句柄
FOREGROUND_BLUE: 字体颜色:蓝控制台文本上色demo代码如下所示。
FOREGROUND_GREEN: 字体颜色:绿
FOREGROUND_RED: 字体颜色:红
FOREGROUND_INTENSITY: 前景色高亮显示
BACKGROUND_BLUE: 背景颜色:蓝
BACKGROUND_GREEN: 背景颜色:绿
BACKGROUND_RED: 背景颜色:红
BACKGROUND_INTENSITY 背景色高亮显示
下面看一下colored_fputs()函数的源代码。