Mac下编译ijkplayer及脚本分析

项目中如果要实现视频播放,最方便的就是使用B站开源的ijkplayer。本篇文章就是告知怎样编译,且对编译脚本进行详细分析。

Mac下编译ijkplayer

  1. 编译前安装必要软件

    # 安装 homebrew
    ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
    # 安装git
    brew install git
    # 安装yasm
    brew install yasm
    
  2. 配置SDK和NDK路径

    # 把路径的声明放到~/.bash_profile 或者 ~/.profile,我的使用zshrc,所以是放在~/.zshrc
    export ANDROID_SDK=
    export ANDROID_NDK=
    # 设置完成后需要运行
    source ./.zshrc
    

    如果只是编译ijkplayer的时候使用,那么可以在终端设置临时环境变量

    # 这里要使用10到14的版本,原因看下面错误分析
    export ANDROID_SDK=/Users/guidongyuan/Library/Android/sdk
    export ANDROID_NDK=/Users/guidongyuan/Library/Android/android-ndk-r10e
    # 查看是否设置成功
    echo $ANDROID_SDK
    /Users/guidongyuan/Library/Android/sdk
    echo $ANDROID_NDK
    /Users/guidongyuan/Library/Android/android-ndk-r10e
    
  3. 下载ffmpeg

    # 检出项目
    git clone https://github.com/Bilibili/ijkplayer.git ijkplayer-android
    # 进入ijkplayer-android目录
    cd ijkplayer-android
    # 检出最新版本
    git checkout -B latest k0.8.8
    # 安装ffmpeg,上面的检出是没有包含ffmpeg源码的,这里运行shell脚本去检出,具体看下面分析
    ./init-android.sh
    
  4. 选择配置文件

    # If you prefer more codec/format
    cd config
    # 移除上面init-android.sh生成的module.sh(默认为module-lite.sh)
    rm module.sh
    # 创建module.sh引用,引用到module-default.sh中,有点类似pc上的创建桌面快捷方式
    ln -s module-default.sh module.sh
    
  5. 编译ffmpeg

    cd android/contrib
    # 清除ffmpeg的编译文件
    # 如果出错,看下面错误分析
    ./compile-ffmpeg.sh clean
    # 编译ffmpeg的所有架构,如果只需要编译armv7a,那么就把all改为armv7a
    ./compile-ffmpeg.sh all
    

    编译完成后,在contrib/build/文件夹下会生成不同架构的文件夹,架构文件夹中的output目录下可以看到生成的libijkffmpeg.so文件,如下图

    Mac下编译ijkplayer及脚本分析_第1张图片
  6. 编译ijkplayer

    cd ..
    ./compile-ijk.sh all
    

    编译完成后,在android/ijkplayer文件夹的架构项目库中,可以看到生成的libijkplayer.so、libijkffmpeg.so、libijksdl.so这三个文件被放在项目的/src/main/libs/架构名/下了。

sh脚本分析

  • ./init-android.sh

    # 截取部分代码
    # 执行tools目录下的pull-repo-base.sh脚本,去拉取ffmpeg的代码到extra/ffmpeg下
    # pull-repo-base.sh脚本不难分析,传入git地址和检出的路径
    sh $TOOLS/pull-repo-base.sh $IJK_FFMPEG_UPSTREAM $IJK_FFMPEG_LOCAL_REPO
    
    function pull_fork()
    {
        echo "== pull ffmpeg fork $1 =="
        # $1为传入的平台
        # 同样执行tools目录下的pull-repo-ref.sh脚本去检出不同平台的代码到android/contrib目录下
        sh $TOOLS/pull-repo-ref.sh $IJK_FFMPEG_FORK android/contrib/ffmpeg-$1 ${IJK_FFMPEG_LOCAL_REPO}
        cd android/contrib/ffmpeg-$1
        git checkout ${IJK_FFMPEG_COMMIT} -B ijkplayer
        cd -
    }
    
    # 传入不同的平台,调用上面的方法
    pull_fork "armv5"
    pull_fork "armv7a"
    pull_fork "arm64"
    pull_fork "x86"
    pull_fork "x86_64"
    
    # 初始化设置,会判断config中是否存在module.sh,没有就复制config/module-lite.sh为module.sh
    ./init-config.sh
    # 检出libyuv的代码到ijkmedia/ijkyuv
    ./init-android-libyuv.sh
    # 检出soundtouch代码到ijkmedia/ijksoundtouch
    ./init-android-soundtouch.sh
    
  • compile-ffmpeg.sh

    # 判断传递进来的参数
    case "$FF_TARGET" in
      # 如果不传递,默认为armv7a
      # 然后调用tools/do-compile-ffmpeg.sh真正去执行
        "")
            echo_archs armv7a
            sh tools/do-compile-ffmpeg.sh armv7a
        ;;
        # 根据传入的不同平台类型
        armv5|armv7a|arm64|x86|x86_64)
            echo_archs $FF_TARGET $FF_TARGET_EXTRA
            sh tools/do-compile-ffmpeg.sh $FF_TARGET $FF_TARGET_EXTRA
            echo_nextstep_help
        ;;
        all32)
            echo_archs $FF_ACT_ARCHS_32
            for ARCH in $FF_ACT_ARCHS_32
            do
                sh tools/do-compile-ffmpeg.sh $ARCH $FF_TARGET_EXTRA
            done
            echo_nextstep_help
        ;;
        # 如果是all的话,会循环编译各个平台
        all|all64)
            echo_archs $FF_ACT_ARCHS_64
            for ARCH in $FF_ACT_ARCHS_64
            do
                sh tools/do-compile-ffmpeg.sh $ARCH $FF_TARGET_EXTRA
            done
            echo_nextstep_help
        ;;
        # 如果是clean,会循环执行删除各个平台编译生成的文件
        clean)
            echo_archs FF_ACT_ARCHS_64
            for ARCH in $FF_ACT_ARCHS_ALL
            do
                if [ -d ffmpeg-$ARCH ]; then
                    cd ffmpeg-$ARCH && git clean -xdf && cd -
                fi
            done
            rm -rf ./build/ffmpeg-*
        ;;
    
  • compile-ijk.sh

    # 代码顺序进行调整省略
    # 判断不同的平台架构,然后进入"ijkplayer/ijkplayer-平台架构名称/src/main/jni"文件,执行下面的do_sub_cmd方法
    do_ndk_build () {
        case "$PARAM_TARGET" in
            armv5|armv7a)
                cd "ijkplayer/ijkplayer-$PARAM_TARGET/src/main/jni"
                do_sub_cmd $PARAM_SUB_CMD
                cd -
            ;;
        esac
    }
    do_sub_cmd () {
            echo 'profiler build: NO';
            # 在上面进去的jni目录下,创建ijkplayer-android/ijkprof/android-ndk-profiler-dummy/jni目录的引用
            ln -s ../../../../../../ijkprof/android-ndk-profiler-dummy/jni android-ndk-prof
        fi
    
        case $SUB_CMD in
            prof)
              # 利用ndk进行编译
                $ANDROID_NDK/ndk-build $FF_MAKEFLAGS
            ;;
        esac
    }
    

    compile-ijk.sh脚本的执行,其实就是进去到不同的平台架构库(如ijkplayer-armv7a),利用makefile进行构建,编译生成libs目录下的libijkplayer.so、libijkffmpeg.so、libijksdl.so三个文件。
    其中平台架构下的源码,都是引用其他路径的,比如ijkplayer-armv7a/src/main/jni下的ijkmedia,是引用到ijkplayer-android/ijkmedia目录下的。
    ffmpeg.so库,是引用上一步编译ffmpeg输出到ijkplayer-android/android/contrib/build/ffmpeg-armv7a/output/的so库。
    在没编译之前,一直不太理解为啥要有那么多依赖库,还以为上面的三个动态库是类似编译ffmpeg直接用sh编译的,原来是使用到Android中的make进行构建编译。

导入项目

so库编译完成后,打开Android studio导入android/ijkplayer/的内容,就可以编译运行demo看看效果了。

出错问题

  • NDK版本不符合导致错误,提示内容为You need the NDKr10e or later

    我本地的NDK版本为17,重新下载NDK为10的版本,就没有问题了。出错的原因,可以在do-detect-env.sh看到,这是一个环境检查的脚本

    # 可以看到,ndk版本为10是去读取RELEASE.TXT文件内容的版本号进行正则匹配
    export IJK_NDK_REL=$(grep -o '^r[0-9]*.*' $ANDROID_NDK/RELEASE.TXT 2>/dev/null | sed 's/[[:space:]]*//g' | cut -b2-)
    case "$IJK_NDK_REL" in
        10e*)
    # 而11到14的版本,是读取source.properties的内容,我本地是17版本,估计那个时候还没有吧,才导致编译出错
    IJK_NDK_REL=$(grep -o '^Pkg\.Revision.*=[0-9]*.*' $ANDROID_NDK/source.properties 2>/dev/null | sed 's/[[:space:]]*//g' | cut -d "=" -f 2)
            echo "IJK_NDK_REL=$IJK_NDK_REL"
            case "$IJK_NDK_REL" in
                11*|12*|13*|14*)
    

    解决办法

    在NDK官网下载版本为10到14,重新设置路径的环境变量就没有问题

  • 在执行compile-ffmpeg.sh时候提示找不到perf_event.h文件,提示内容为./libavutil/timer.h:38:31: fatal error: linux/perf_event.h: No such file or directory

    查看timer.h可以看到如下声明

    # 如果定义了CONFIG_LINUX_PERF,那么就需要使用到perf_event.h
    #if CONFIG_LINUX_PERF
    # ifndef _GNU_SOURCE
    #  define _GNU_SOURCE
    # endif
    # include  // read(3)
    # include 
    # include 
    # include 
    #endif
    

    解决办法

    修改module.sh文件,添加export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-linux-perf"

    看ijkplayer的issue中,有网友说还需要添加export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bzlib"文件,测试一下值需要添加上面一句就可以了,也有可能是我之前弄过zlib,也有可能是不需要的。

    注意:

    在修改后,需要先执行清除ffmpeg的编译文件./compile-ffmpeg.sh clean再执行./compile-ffmpeg.sh all

  • 导入到AS中,如果编译运行出现错误,错误内容为

    No toolchains found in the NDK toolchains folder for ABI with prefix: mips64el-linux-android

    出现该错误,是因为需要使用mips64el-linux-android目录的内容,而AS中使用的NDK是在SDK内的,缺少了该目录

    解决办法

    直接去NDK官网下载完整的文件,然后把toolchains目录下的mips64el-linux-android拷贝过去就可以了。详细步骤可以参考完美解决 No toolchains found in the NDK toolchains folder for ABI with prefix: mips64el-linux-android。

Mac下编译ijkplayer及脚本分析_第2张图片

你可能感兴趣的:(Mac下编译ijkplayer及脚本分析)