Android 集成 ijkplayer 编译及替换 cmake

作者: 王英豪 | 来源:发表于2019-02-19 11:18 被阅读5次

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

    待续...

    相关文章

      网友评论

        本文标题:Android 集成 ijkplayer 编译及替换 cmake

        本文链接:https://www.haomeiwen.com/subject/uosxyqtx.html