Android 集成 ijkplayer 编译及替换 cmake 方式编译

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"),可去除冗余的二进制数据。

待续...

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271