ijkplayer 的常规编译步骤记录:
编译并运行demo:
1.clone
git clone https://github.com/Bilibili/ijkplayer.git ijkplayer-android
2.初始化,下载ffmpeg等源码
cd ijkplayer-android
./init-android.sh
3.编译ffmpeg(需要配置ANDROID_NDK,ANDROID_SDK环境变量)
cd android/contrib
./compile-ffmpeg.sh clean
./compile-ffmpeg.sh all
4.编译.so
cd ..
./compile-ijk.sh all
5.运行
AS打开 ijkplayer-android/android/ijkplayer 即可
ijk结构:
libijkffmpeg.so
libijksdl.so
libijkplayer.so
1.libijkplayer.so
LOCAL_SHARED_LIBRARIES := ijkffmpeg ijksdl
LOCAL_STATIC_LIBRARIES := android-ndk-profiler ijksoundtouch
2.libijksdl.so
LOCAL_SHARED_LIBRARIES := ijkffmpeg
LOCAL_STATIC_LIBRARIES := cpufeatures yuv_static ijkj4a
3.libijkffmpeg.so
步骤:
1.编译libijkffmpeg.so
2.编译cpufeatures yuv_static ijkj4a静态库
3.编译libijksdl.so
4.编译android-ndk-profiler ijksoundtouch 静态库
5.编译libijkplayer.so
MTXXVideoEditor步骤
1.cd videoeditor-generate_so/ffmpeg
2.sh compile.sh
j4a安装测试
1.git clone https://github.com/Bilibili/jni4android.git jni4android
2.cd jni4android
3.
# build dependencies
# you don't have to run this if you have bison 3.x installed
./get-deps.sh
报错:configure: error: no acceptable m4 could be found in $PATH
解决:http://blog.csdn.net/ldl22847/article/details/8575140
报错: Makefile:64: recipe for target 'src/flex.j4a.yy.cpp' failed flex:命令未找到
解决:sudo apt-get install flex bison
4.
./configure
5.
make
报错:clang++:命令未找到
解决:sudo apt-get install clang
ijkj4a: cmake 没体现 : $(call import-module,android/cpufeatures), j4a中没有用到cpufeatures,为什么要导入?
libyuv: 大小不一样
cpufeatures: 什么时候生成的? 执行: ./compile-ijk.sh armv7a时生成 即 ndk-build ,关键语法:call import-module
cmake中可以通过include(AndroidNdkModules) android_ndk_import_module_cpufeatures() 导入,但导入后的模块为android.mk编译方式
解决方案:将这个模块源码拷贝到工程中,转为cmake编译方式,不再需要android_ndk_import_module_cpufeatures导入
静态库拷贝:
#是否需要从externalNativeBuild目录中拷贝静态库到输出目录, 设置为"true"时才会拷贝
#暂时用此方式解决静态库无法输出到指定目录的问题
set(static_cp
"false"
)
if(${static_cp} STREQUAL "true")
add_custom_command(TARGET ${lib_name}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_SOURCE_DIR}/.externalNativeBuild/cmake/debug/${ANDROID_ABI}/${lib_name}/lib${lib_name}.a
${CMAKE_SOURCE_DIR}/libs/${abi_library_name})
endif()
sdl编译问题:
x86成功编译,armv7a报错:Error:(201) undefined reference to 'I422ToYUY2Row_NEON' 等等等等
查看对应的实现源文件,全部褐色
问题:set(cxx_flag
${cxx_flag}
-DLIBYUV_NEON
)
解决:set(cxx_flag
${cxx_flag}
-LIBYUV_NEON
)
application.h
ijkffmpeg.so 2.2M
ijkplayer 200+k
ijksdl 200+k
问题:
Error:Execution failed for task ':videoeditor-armv7a:transformNativeLibsWithMergeJniLibsForDebug'.
> More than one file was found with OS independent path 'lib/armeabi-v7a/libijkplayer.so'
原因:
videoeditor_armv7a 为cmake工程,自动把gradle中的ijksdl、ijkplayer打到apk中了,与 jniLibs.srcDirs冲突
解决:
jniLibs.srcDirs只指向 libffmpeg.so
IjkPlayer 源码是用 Android.mk 方式编译的,为了方便开发,需要将其编译方式替换为 CMake,过程中遇到了很多让人头疼的问题,好在最后还是成功替换了~
举个例子,在编译 IjkPlayer 源码中的 sdl 动态库时,其源码中的 Android.mk 方式如下:
LOCAL_PATH := $(call my-dir)
# 以预编译库的方式引入编译 sdl 库依赖的动态/静态库
include $(CLEAR_VARS)
LOCAL_MODULE := ijkffmpeg
LOCAL_SRC_FILES := $(LOCAL_PATH)/../libffmpeg.so
#LOCAL_SRC_FILES := /home/yhao/ijkplayer-android/android/contrib/build/ffmpeg-armv7a/output/libijkffmpeg.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := cpufeatures
LOCAL_SRC_FILES := $(LOCAL_PATH)/../libcpufeatures.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := yuv_static
LOCAL_SRC_FILES := $(LOCAL_PATH)/../libyuv_static.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := ijkj4a
LOCAL_SRC_FILES := $(LOCAL_PATH)/../libijkj4a.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
# 编译选项
LOCAL_CFLAGS += -std=c99
# 需要链接的库
LOCAL_LDLIBS += -llog -landroid -lOpenSLES -lEGL -lGLESv2
# 需要引入的头文件
sdl_dir=/home/yhao/mtxxvideoeditor/videoeditor-generate_so/ijksdl
ffmpeg_include_dir=/home/yhao/mtxxvideoeditor/videoeditor-generate_so/ffmpeg/output/armv7a/include
ijkyuv_include_dir=/home/yhao/mtxxvideoeditor/videoeditor-generate_so/ijkyuv/include
ijkj4a_include_dir=/home/yhao/mtxxvideoeditor/videoeditor-generate_so/ijkj4a
LOCAL_C_INCLUDES += $(sdl_dir)
LOCAL_C_INCLUDES += $(realpath $(sdl_dir)/..)
LOCAL_C_INCLUDES += $(ffmpeg_include_dir)
LOCAL_C_INCLUDES += $(ijkyuv_include_dir)
LOCAL_C_INCLUDES += $(ijkj4a_include_dir)
# 需要编译的源码文件
LOCAL_SRC_FILES += ijksdl_aout.c
LOCAL_SRC_FILES += ijksdl_audio.c
LOCAL_SRC_FILES += ijksdl_egl.c
LOCAL_SRC_FILES += ijksdl_error.c
LOCAL_SRC_FILES += ijksdl_mutex.c
LOCAL_SRC_FILES += ijksdl_stdinc.c
LOCAL_SRC_FILES += ijksdl_thread.c
LOCAL_SRC_FILES += ijksdl_timer.c
LOCAL_SRC_FILES += ijksdl_vout.c
LOCAL_SRC_FILES += ijksdl_extra_log.c
LOCAL_SRC_FILES += gles2/color.c
LOCAL_SRC_FILES += gles2/common.c
LOCAL_SRC_FILES += gles2/renderer.c
LOCAL_SRC_FILES += gles2/renderer_rgb.c
LOCAL_SRC_FILES += gles2/renderer_yuv420p.c
LOCAL_SRC_FILES += gles2/renderer_yuv444p10le.c
LOCAL_SRC_FILES += gles2/shader.c
LOCAL_SRC_FILES += gles2/fsh/rgb.fsh.c
LOCAL_SRC_FILES += gles2/fsh/yuv420p.fsh.c
LOCAL_SRC_FILES += gles2/fsh/yuv444p10le.fsh.c
LOCAL_SRC_FILES += gles2/vsh/mvp.vsh.c
LOCAL_SRC_FILES += dummy/ijksdl_vout_dummy.c
LOCAL_SRC_FILES += ffmpeg/ijksdl_vout_overlay_ffmpeg.c
LOCAL_SRC_FILES += ffmpeg/abi_all/image_convert.c
LOCAL_SRC_FILES += android/android_audiotrack.c
LOCAL_SRC_FILES += android/android_nativewindow.c
LOCAL_SRC_FILES += android/ijksdl_android_jni.c
LOCAL_SRC_FILES += android/ijksdl_aout_android_audiotrack.c
LOCAL_SRC_FILES += android/ijksdl_aout_android_opensles.c
LOCAL_SRC_FILES += android/ijksdl_codec_android_mediacodec_dummy.c
LOCAL_SRC_FILES += android/ijksdl_codec_android_mediacodec_internal.c
LOCAL_SRC_FILES += android/ijksdl_codec_android_mediacodec_java.c
LOCAL_SRC_FILES += android/ijksdl_codec_android_mediacodec.c
LOCAL_SRC_FILES += android/ijksdl_codec_android_mediadef.c
LOCAL_SRC_FILES += android/ijksdl_codec_android_mediaformat_java.c
LOCAL_SRC_FILES += android/ijksdl_codec_android_mediaformat.c
LOCAL_SRC_FILES += android/ijksdl_vout_android_nativewindow.c
LOCAL_SRC_FILES += android/ijksdl_vout_android_surface.c
LOCAL_SRC_FILES += android/ijksdl_vout_overlay_android_mediacodec.c
# 依赖编译 sdl 库所需的动态/静态库
LOCAL_SHARED_LIBRARIES := ijkffmpeg
LOCAL_STATIC_LIBRARIES := cpufeatures yuv_static ijkj4a
LOCAL_MODULE := ijksdl
include $(BUILD_SHARED_LIBRARY)
替换为 CMake 方式:
cmake_minimum_required(VERSION 3.4.1)
# 获取当前目录
set(pwd_dir ${CMAKE_SOURCE_DIR}/ijksdl)
# 设置库名称
set(lib_name ijksdl)
# 获取库输出目录
set(lib_output_dir
${global_lib_output_dir}
)
# 设置静态库输出目录(参考googlesamples/android-ndk)
set(static_output
"false"
)
# 设置头文件
set(include_dir
${pwd_dir}
${pwd_dir}/..
${ffmpeg_output_include_dir}
${CMAKE_SOURCE_DIR}/ijkyuv/include
${CMAKE_SOURCE_DIR}/ijkj4a
)
# 以预编译库的方式引入编译 sdl 库依赖的动态/静态库
add_library(cpufeatures-lib STATIC IMPORTED)
set_target_properties(cpufeatures-lib PROPERTIES IMPORTED_LOCATION
${lib_output_dir}/libcpufeatures.a)
add_library(yuv_static-lib STATIC IMPORTED)
set_target_properties(yuv_static-lib PROPERTIES IMPORTED_LOCATION
${lib_output_dir}/libyuv_static.a)
add_library(ijkj4a-lib STATIC IMPORTED)
set_target_properties(ijkj4a-lib PROPERTIES IMPORTED_LOCATION
${lib_output_dir}/libijkj4a.a)
add_library(ffmpeg-lib SHARED IMPORTED)
set_target_properties(ffmpeg-lib PROPERTIES IMPORTED_LOCATION
${ffmpeg_output_shared_lib})
# 兼容gl版本
message(FATAL_DEBUG "兼容gl版本 \
(currently using ${ANDROID_PLATFORM_LEVEL}).")
if (${ANDROID_PLATFORM_LEVEL} LESS 12)
message(FATAL_ERROR "OpenGL 2 is not supported before API level 11")
return()
elseif (${ANDROID_PLATFORM_LEVEL} LESS 18)
message(FATAL_DEBUG "add_definitions DDYNAMIC_ES3 ")
add_definitions("-DDYNAMIC_ES3")
set(GL3STUB_SRC ${pwd_dir}/gles2/gl3stub.c)
set(OPENGL_LIB GLESv2)
else ()
message(FATAL_DEBUG "GLESv3 无法提前获取gl版本,无法动态引入头文件,故平台版本需固定,低于18 ")
set(OPENGL_LIB GLESv3)
endif (${ANDROID_PLATFORM_LEVEL} LESS 12)
#将若干库链接到目标库文件
set(link_lib
${OPENGL_LIB}
log
android
OpenSLES
EGL
ffmpeg-lib
cpufeatures-lib
yuv_static-lib
ijkj4a-lib
)
#设置编译选项
set(cmake_c_flag_debug
-std=c99
)
set(cmake_cxx_flag_debug
)
# 设置要编译的源文件
set(source_files
${GL3STUB_SRC}
${pwd_dir}/gles2/gl_util.c
${pwd_dir}/gles2/ff_ffmux_soft.c
${pwd_dir}/gles2/ff_ffmux_hard.c
${pwd_dir}/gles2/ff_print_util.c
${pwd_dir}/gles2/ff_converter.c
${pwd_dir}/gles2/ff_ffmusic_decode.c
${pwd_dir}/gles2/ff_audio_converter.c
${pwd_dir}/ijksdl_aout.c
${pwd_dir}/ijksdl_audio.c
${pwd_dir}/ijksdl_egl.c
${pwd_dir}/ijksdl_error.c
${pwd_dir}/ijksdl_mutex.c
${pwd_dir}/ijksdl_stdinc.c
${pwd_dir}/ijksdl_thread.c
${pwd_dir}/ijksdl_timer.c
${pwd_dir}/ijksdl_vout.c
${pwd_dir}/ijksdl_extra_log.c
${pwd_dir}/gles2/color.c
${pwd_dir}/gles2/common.c
${pwd_dir}/gles2/renderer.c
${pwd_dir}/gles2/renderer_rgb.c
${pwd_dir}/gles2/renderer_yuv420p.c
${pwd_dir}/gles2/renderer_yuv444p10le.c
${pwd_dir}/gles2/shader.c
${pwd_dir}/gles2/fsh/rgb.fsh.c
${pwd_dir}/gles2/fsh/yuv420p.fsh.c
${pwd_dir}/gles2/fsh/yuv420pmeitu.fsh.c
${pwd_dir}/gles2/fsh/yuv444p10le.fsh.c
${pwd_dir}/gles2/vsh/mvp.vsh.c
${pwd_dir}/dummy/ijksdl_vout_dummy.c
${pwd_dir}/ffmpeg/ijksdl_vout_overlay_ffmpeg.c
${pwd_dir}/ffmpeg/abi_all/image_convert.c
${pwd_dir}/android/android_audiotrack.c
${pwd_dir}/android/android_nativewindow.c
${pwd_dir}/android/ijksdl_android_jni.c
${pwd_dir}/android/ijksdl_aout_android_audiotrack.c
${pwd_dir}/android/ijksdl_aout_android_opensles.c
${pwd_dir}/android/ijksdl_codec_android_mediacodec_dummy.c
${pwd_dir}/android/ijksdl_codec_android_mediacodec_internal.c
${pwd_dir}/android/ijksdl_codec_android_mediacodec_java.c
${pwd_dir}/android/ijksdl_codec_android_mediacodec.c
${pwd_dir}/android/ijksdl_codec_android_mediadef.c
${pwd_dir}/android/ijksdl_codec_android_mediaformat_java.c
${pwd_dir}/android/ijksdl_codec_android_mediaformat.c
${pwd_dir}/android/ijksdl_vout_android_nativewindow.c
${pwd_dir}/android/ijksdl_vout_android_surface.c
${pwd_dir}/android/ijksdl_vout_overlay_android_mediacodec.c
)
# 设置动态库输出路径
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${lib_output_dir})
# 需要引入的头文件
include_directories(${include_dir})
# 设置编译选项
set(CMAKE_C_FLAGS_DEBUG "${cmake_c_flag_debug} -s")
set(CMAKE_CXX_FLAGS_DEBUG "${cmake_cxx_flag_debug} -s")
# 设置要编译的源文件
add_library(${lib_name} SHARED ${source_files})
# 设置依赖的库
target_link_libraries(${lib_name} ${link_lib})
# 是否需要输出静态库
if(${static_output} STREQUAL "true")
set_target_properties(${lib_name}
PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY
${lib_output_dir})
endif()
CMake 编译方式中添加了一些兼容 gl 的处理及源文件,可以暂时忽略,语法上与 Android.mk 方式大同小异。
除了 sdl 库外,还需要编译 ijkyuv、ijk4a、ijkplayer 等库,这些库和 sdl 库同级目录,每个库中都需配置自己的 CMakeLists.txt 文件,在最外层的配置 CMakeLists.txt 文件中添加编译子目录,如下:
add_subdirectory(${CMAKE_SOURCE_DIR}/cpufeatures)
add_subdirectory(${CMAKE_SOURCE_DIR}/ijkj4a)
add_subdirectory(${CMAKE_SOURCE_DIR}/ijkyuv)
add_subdirectory(${CMAKE_SOURCE_DIR}/ijksdl)
add_subdirectory(${CMAKE_SOURCE_DIR}/android-ndk-prof)
add_subdirectory(${CMAKE_SOURCE_DIR}/ijksoundtouch)
add_subdirectory(${CMAKE_SOURCE_DIR}/ijkplayer)
然后在应用的 build.gradle 中指定最外层的 CMakeLists.txt 即可,另外也可以配置编译选项:
defaultConfig {
省略...
// 配置编译选项
externalNativeBuild {
cmake {
cppFlags " -O3 -Wall -pipe " +
" -ffast-math " +
" -fstrict-aliasing -Werror=strict-aliasing" +
" -Wno-psabi -Wa,--noexecstack " +
" -DANDROID -DNDEBUG"
cFlags " -O3 -Wall -pipe " +
" -ffast-math " +
" -fstrict-aliasing -Werror=strict-aliasing" +
" -Wno-psabi -Wa,--noexecstack " +
" -DANDROID -DNDEBUG"
arguments "-DANDROID_STL=stlport_static",
"-DANDROID_PIE=OFF",
"-DANDROID_ARM_NEON=TRUE",
"-DANDROID_PLATFORM=android-15"
targets "cpufeatures",
"ijkj4a",
"yuv_static",
"ijksdl",
"android-ndk-profiler",
"ijksoundtouch",
"ijkplayer"
}
}
ndk {
abiFilters 'armeabi-v7a'
}
}
编译选项记录:
-Wall 是打开警告开关
-O1 提供基础级别的优化
-O2 提供更加高级的代码优化,会占用更长的编译时间
-O3 提供最高级的代码优化
-ffast-math 对于这些以速度为重的应用,该选项定义了预处理器宏 FAST_MATH, 指示编译不必遵循 IEEE 和 ISO 的浮点运算标准
-fstrict-aliasing 在编译选项中加入-fstrict-aliasing的优势在于向编译器说明不同类型的lvalue将指向不相关的内存区域,编译器可以做大量的优化。
-fno-strict-aliasing:在编译内核的编译选项CFLAGS中,加入了-fno-strict-aliasing,向编译器表明不同类型的lvalue可能指向相关的内存区域,
因此编译器不会做出一些极端的优化而造成不安全(内核编译中优化选项为-O2, -O2优化时默认是-fstrict-aliasing,因此需要显式的指出编译参数是-fno-strict-aliasing)
-Werror=strict-aliasing : http://clhjoe.blogspot.hk/2012/06/gcc-strict-aliasing.html
-Wno-psabi 可以防止ndk-build编译时出现的警告。
-Wall 选项意思是编译后显示所有警告。
-W 选项类似-Wall,会显示警告,但是只显示编译器认为会出现错误的警告。
CMake 问题记录:
Q:编译 libyuv 库时报错:Error:(201) undefined reference to 'I422ToYUY2Row_NEON'
A:编译选项 -DLIBYUV_NEON 改为 -LIBYUV_NEON,CMake 方式不同于 Android.mk 方式。
Q:相比与 Android.mk 方式,CMake 方式编译出的动态库体积很大,怎么办?
A:编译选项末尾加上 -s,形如: set(CMAKE_C_FLAGS_DEBUG "${cmake_c_flag_debug} -s"),可去除冗余的二进制数据。
待续...