Ubuntu编译ijkplayer

1. 安装ndk
   最新版本NDK可到官网下载:https://developer.android.google.cn/ndk/downloads/
   各历史版本的下载可参考博客:https://blog.csdn.net/gyh198/article/details/75036686
   下载完成后先解压(解压位置自定),然后配置环境变量:
   vim ~/.bashrc
   在最后面添加如下配置信息:
   ANDROID_NDK=/home/xxxxxxx/xxxx/ndk/android-ndk-r10e
   export ANDROID_NDK
   PATH=${PATH}:${ANDROID_NDK}

2. clone ijkplayer源码
   git clone https://github.com/Bilibili/ijkplayer.git

   初识代码结构(参考博客:https://www.jianshu.com/p/daf0a61cc1e0):
   ijkplayer/
    -- android/  - android平台上的上层接口封装以及平台相关方法
    -- config/  - 编译ffmpeg使用的配置文件
    -- doc/
    -- extra/  - 存放编译ijkplayer所需的依赖源文件, 如ffmpeg、libyuv及soundtouch等
    -- ijkmedia/  - 核心代码
        -- ijkplayer/  - 播放器数据下载及解码相关
        -- ijksdl/  - 音视频数据渲染相关
    -- ijkprof/
    -- ios/  - iOS平台上的上层接口封装以及平台相关方法
    -- tools/  - 初始化项目工程脚本

3. compile ijkplayer
   step 1: 初始化
   cd ijkplayer
   ./init-android.sh

   说明:查看init-android.sh源码:

...
function pull_fork()
{
    ...
    sh $TOOLS/pull-repo-ref.sh $IJK_FFMPEG_FORK android/contrib/ffmpeg-$1 ${IJK_FFMPEG_LOCAL_REPO}
    ...
}

...

./init-config.sh
./init-android-libyuv.sh
./init-android-soundtouch.sh

    再查看tools/pull-repo-ref.sh源码:

if [ -z $1 -o -z $2 -o -z $3 ]; then
    echo "invalid call pull-repo.sh '$1' '$2' '$3'"
elif [ ! -d $LOCAL_WORKSPACE ]; then
    git clone --reference $REF_REPO $REMOTE_REPO $LOCAL_WORKSPACE
    cd $LOCAL_WORKSPACE
    git repack -a
else
    cd $LOCAL_WORKSPACE
    git fetch --all --tags
    cd -
fi

    其中最重要的便是git clone --reference $REF_REPO $REMOTE_REPO $LOCAL_WORKSPACE,所以实际是在clone ffmpeg源码。再看init-android-libyuv.sh和init-android-soundtouch.sh的源码,会发现其实也是调用了tools/pull-repo-ref.sh分别去克隆libyuv和soundtouch的源码:

...
echo "== pull libyuv base =="
sh $TOOLS/pull-repo-base.sh $IJK_LIBYUV_UPSTREAM $IJK_LIBYUV_LOCAL_REPO

echo "== pull libyuv fork =="
sh $TOOLS/pull-repo-ref.sh $IJK_LIBYUV_FORK ijkmedia/ijkyuv ${IJK_LIBYUV_LOCAL_REPO}
cd ijkmedia/ijkyuv
git checkout ${IJK_LIBYUV_COMMIT}
cd -
...
echo "== pull soundtouch base =="
sh $TOOLS/pull-repo-base.sh $IJK_SOUNDTOUCH_UPSTREAM $IJK_SOUNDTOUCH_LOCAL_REPO

echo "== pull soundtouch fork =="
sh $TOOLS/pull-repo-ref.sh $IJK_SOUNDTOUCH_FORK ijkmedia/ijksoundtouch ${IJK_SOUNDTOUCH_LOCAL_REPO}
cd ijkmedia/ijksoundtouch
git checkout ${IJK_SOUNDTOUCH_COMMIT}
cd -

    以下编译日志的关键信息,可以看到确实是在clone ffmpeg, libyuv及soundtouch的源码:

    == pull ffmpeg base ==
    Cloning into 'extra/ffmpeg'...
    ...
    == pull ffmpeg fork armv5 ==
    Cloning into 'android/contrib/ffmpeg-armv5'...
    ...
    == pull ffmpeg fork armv7a ==
    Cloning into 'android/contrib/ffmpeg-armv7a'...
    ...
    == pull ffmpeg fork arm64 ==
    Cloning into 'android/contrib/ffmpeg-arm64'...
    ...
    == pull ffmpeg fork x86 ==
    Cloning into 'android/contrib/ffmpeg-x86'...
    ...
    == pull ffmpeg fork x86_64 ==
    Cloning into 'android/contrib/ffmpeg-x86_64'...
    ...
    == pull libyuv base ==
    Cloning into 'extra/libyuv'...
    ...
    == pull libyuv fork ==
    Cloning into 'ijkmedia/ijkyuv'...
    ...
    == pull soundtouch base ==
    Cloning into 'extra/soundtouch'...
    ...
    == pull soundtouch fork ==
    Cloning into 'ijkmedia/ijksoundtouch'...
    ...

   step 2: 编译ffmpeg
   cd android/contrib
   ./compile-ffmpeg.sh clean
   ./compile-ffmpeg.sh all

    说明:查看compile-ffmpeg.sh源码:

...
case "$FF_TARGET" in
    "")
        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|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)
        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-*
    ;;
    check)
        echo_archs FF_ACT_ARCHS_ALL
    ;;
    *)
        echo_usage
        exit 1
    ;;
esac

    由源码可知,./compile-ffmpeg.sh clean实际执行的是rm -rf ./build/ffmpeg-*,即清空build目录。另外从源码可以发现,如果执行clean时带上-d参数,就会执行git clean -df,熟悉git的同学都知道这句命令是在清除本地代码与服务器代码之间的差异,如果想让本地ffmpeg与服务器代码保持一致,就可以在执行时带上-d,反之,如果需要保留本地对ffmpeg的修改,则千万不能带上-d,否则就万劫不复了。
    而./compile-ffmpeg.sh all实际上执行的是tools/do-compile-ffmpeg.sh,查看其源码:

...
cd $FF_SOURCE
if [ -f "./config.h" ]; then
    echo 'reuse configure'
else
    which $CC
    ./configure $FF_CFG_FLAGS \
        --extra-cflags="$FF_CFLAGS $FF_EXTRA_CFLAGS" \
        --extra-ldflags="$FF_DEP_LIBS $FF_EXTRA_LDFLAGS"
    make clean
fi

...
cp config.* $FF_PREFIX
make $FF_MAKE_FLAGS > /dev/null
make install
...

    ./configure --> make --> make install,这是Linux标准的编译源码流程。
    编译完成之后查看./build/目录,可以看到ffmpeg-arm64, ffmpeg-armv5, ffmpeg-armv7a, ffmpeg-x86, ffmpeg-x86_64五个编译输出目录,每个目录下都有一个output/libijkffmpeg.so,这个so就会在后面编译ijkplayer时用到。

    以下是编译日志,关键信息不多:
    ...
    /home/xxxxx/xxxxxxxxx/ijkplayer/android/contrib/build/ffmpeg-armv5/toolchain/bin//arm-linux-androideabi-gcc
    install prefix            /home/xxxxx/xxxxxxxxx/ijkplayer/android/contrib/build/ffmpeg-armv5/output
    ...
    /home/xxxxx/xxxxxxxxx/ijkplayer/android/contrib/build/ffmpeg-armv7a/toolchain/bin//arm-linux-androideabi-gcc
    install prefix            /home/xxxxx/xxxxxxxxx/ijkplayer/android/contrib/build/ffmpeg-armv7a/output
    ...
    /home/xxxxx/xxxxxxxxx/ijkplayer/android/contrib/build/ffmpeg-arm64/toolchain/bin//aarch64-linux-android-gcc
    install prefix            /home/xxxxx/xxxxxxxxx/ijkplayer/android/contrib/build/ffmpeg-arm64/output
    ...
    /home/xxxxx/xxxxxxxxx/ijkplayer/android/contrib/build/ffmpeg-x86/toolchain/bin//i686-linux-android-gcc
    install prefix            /home/xxxxx/xxxxxxxxx/ijkplayer/android/contrib/build/ffmpeg-x86/output
    ...
    /home/xxxxx/xxxxxxxxx/ijkplayer/android/contrib/build/ffmpeg-x86_64/toolchain/bin//x86_64-linux-android-gcc
    install prefix            /home/xxxxx/xxxxxxxxx/ijkplayer/android/contrib/build/ffmpeg-x86_64/output
    ...

    注意,编译时若报错:ffmpeg yasm not found, use --disable-yasm for a crippled build,只需要安装yasm(sudo apt-get install yasm),重新编译即可。

   step 3:编译ijkplayer
   cd ..
   ./compile-ijk.sh all

    说明:查看compile-ijk.sh源码如下:

...
do_sub_cmd () {
    SUB_CMD=$1
    ...

    case $SUB_CMD in
        prof)
            $ANDROID_NDK/ndk-build $FF_MAKEFLAGS
        ;;
        clean)
            $ANDROID_NDK/ndk-build clean
        ;;
        rebuild)
            $ANDROID_NDK/ndk-build clean
            $ANDROID_NDK/ndk-build $FF_MAKEFLAGS
        ;;
        *)
            $ANDROID_NDK/ndk-build $FF_MAKEFLAGS
        ;;
    esac
}

do_ndk_build () {
    PARAM_TARGET=$1
    PARAM_SUB_CMD=$2
    case "$PARAM_TARGET" in
        armv5|armv7a)
            cd "ijkplayer/ijkplayer-$PARAM_TARGET/src/main/jni"
            do_sub_cmd $PARAM_SUB_CMD
            cd -
        ;;
        arm64|x86|x86_64)
            cd "ijkplayer/ijkplayer-$PARAM_TARGET/src/main/jni"
            if [ "$PARAM_SUB_CMD" = 'prof' ]; then PARAM_SUB_CMD=''; fi
            do_sub_cmd $PARAM_SUB_CMD
            cd -
        ;;
    esac
}

case "$REQUEST_TARGET" in
    "")
        do_ndk_build armv7a;
    ;;
    armv5|armv7a|arm64|x86|x86_64)
        do_ndk_build $REQUEST_TARGET $REQUEST_SUB_CMD;
    ;;
    all32)
        for ABI in $ACT_ABI_32
        do
            do_ndk_build "$ABI" $REQUEST_SUB_CMD;
        done
    ;;
    all|all64)
        for ABI in $ACT_ABI_64
        do
            do_ndk_build "$ABI" $REQUEST_SUB_CMD;
        done
    ;;
    clean)
        for ABI in $ACT_ABI_ALL
        do
            do_ndk_build "$ABI" clean;
        done
    ;;
    *)
    ...
    ;;
esac

    从源码中不难看出,编译实际上是进入到不同平台(例如ijkplayer/ijkplayer-armv7a)的src/main/jni/止目录,然后调用NDK的工具ndk_build来执行编译,这也说明ijkplayer的核心代码就在该目录下。
    以ikjplayer/ijkplayer-armv7a为例,进入其src/main/jni/目录,发现有ffmpeg/, android-ndk-prof/和ijkmedia/三个目录:
    1) android-ndk-prof是一个链接,android-ndk-prof -> ../../../../../../ijkprof/android-ndk-profiler-dummy/jni/,进入该目录后发现只有prof.c和prof.h两个代码文件,查看其Android.mk文件可知,编译出来是一个名为android-ndk-profiler的静态库(一般是.a文静),猜测应该是为后面编译ijkmedia所用;
    2) ffmpeg/目录下只有一个Android.mk文件,其内容就是引入step2中编译出来的libijkffmpeg.so;
    3) ijkmedia也是一个链接,ijkmedia -> ../../../../../../ijkmedia/,所以实际编译的是ijkplayer根目录下的ijkmedia/,进入到ijkmedia/目录下,可以看到ijkj4a/, ijkplayer/, ijksdl/, ijksoundtouch/及ijkyuv/五个目录,查看每个目录下的Android.mk文件可知,ijkj4a, ijksoundtouch及ijkyuv编出的是静态库(一般是.a文静),而ijkplayer和ijksdl编出来的则是共享库,所以正常编译输出的应该就是libijkplayer.so和libijksdl.so。

    编译完成后,以ijkplayer/ijkplayer-armv7a为为例,进入到src/main/libs/armeabi-v7a/目录,可以看到libijkffmpeg.so, libijkplayer.so及libijksdl.so三个so文件,至此ijkplayer的编译就完成了。

    以下是编译日志的关键信息,可以看到确实是依赖libijkffmpeg.so,输出libijkplayer.so和libijksdl.so:
    ...
    [armeabi] Prebuilt       : libijkffmpeg.so <= /home/xxxxx/xxxxxxxxx/ijkplayer/android/contrib/build/ffmpeg-armv5/output/
    [armeabi] Install        : libijkffmpeg.so => libs/armeabi/libijkffmpeg.so
    ...
    [armeabi] Install        : libijkplayer.so => libs/armeabi/libijkplayer.so
    [armeabi] Install        : libijksdl.so => libs/armeabi/libijksdl.so
    ...
    [armeabi-v7a] Prebuilt       : libijkffmpeg.so <= /home/xxxxx/xxxxxxxxx/ijkplayer/android/contrib/build/ffmpeg-armv7a/output/
    [armeabi-v7a] Install        : libijkffmpeg.so => libs/armeabi-v7a/libijkffmpeg.so
    ...
    [armeabi-v7a] Install        : libijkplayer.so => libs/armeabi-v7a/libijkplayer.so
    [armeabi-v7a] Install        : libijksdl.so => libs/armeabi-v7a/libijksdl.so
    ...
    [arm64-v8a] Prebuilt       : libijkffmpeg.so <= /home/xxxxx/xxxxxxxxx/ijkplayer/android/contrib/build/ffmpeg-arm64/output/
    [arm64-v8a] Install        : libijkffmpeg.so => libs/arm64-v8a/libijkffmpeg.so
    ...
    [arm64-v8a] Install        : libijkplayer.so => libs/arm64-v8a/libijkplayer.so
    [arm64-v8a] Install        : libijksdl.so => libs/arm64-v8a/libijksdl.so
    ...
    [x86] Prebuilt       : libijkffmpeg.so <= /home/xxxxx/xxxxxxxxx/ijkplayer/android/contrib/build/ffmpeg-x86/output/
    [x86] Install        : libijkffmpeg.so => libs/x86/libijkffmpeg.so
    ...
    [x86] Install        : libijkplayer.so => libs/x86/libijkplayer.so
    [x86] Install        : libijksdl.so => libs/x86/libijksdl.so
    ...
    [x86_64] Prebuilt       : libijkffmpeg.so <= /home/xxxxx/xxxxxxxxx/ijkplayer/android/contrib/build/ffmpeg-x86_64/output/
    [x86_64] Install        : libijkffmpeg.so => libs/x86_64/libijkffmpeg.so
    ...
    [x86_64] Install        : libijkplayer.so => libs/x86_64/libijkplayer.so
    [x86_64] Install        : libijksdl.so => libs/x86_64/libijksdl.so
    ...

4. 总结
通过编译ijkplayer, 对其代码结构有了一个新的认识:
ijkplayer/
 -- android/
     -- contrib/
         -- build/  - 存放ffmpeg编译输出的文件,最重要的是ffmpeg-xxxxx/output/libijkffmpeg.so
         -- ffmpeg-arm64/  - ffmpeg源码
         -- ffmpeg-armv5/  - ffmpeg源码
         -- ffmpeg-armv7a/  - ffmpeg源码
         -- ffmpeg-x86/  - ffmpeg源码
         -- ffmpeg-x86_64/  - ffmpeg源码
         -- tools/  - 存放编译ffmpeg的脚本文件
         -- compile-ffmpeg.sh  - 启动编译ffmpeg的脚本
         -- compile-libsoxr.sh
         -- compile-openssl.sh
         -- setup-as-commiter.sh
         -- sync-mirriors.sh
     -- ijkplayer/
         -- gradle/
         -- ijkplayer-arm64/  - ijkplayer源码
         -- ijkplayer-armv5/  - ijkplayer源码
         -- ijkplayer-armv7a/  - ijkplayer源码
             -- src/
                 -- androidTest/
                 -- main/
                     -- java/
                     -- jni/
                         -- android-ndk-prof/  - ln,编出的是静态库
                         -- ffmpeg/  - 通过Android.mk引入libijkffmpeg.so
                         -- ijkmedia/  - ln, 指向根目录的下的ijkmedia, 存放核心代码
                         -- Android.mk
                         -- Application.mk
                     -- libs/  - 存放ijkplayer编译输出的文件,最重要的是libijkffmpeg, libijkplayer.so和libijksdl.so
                     -- obj/
                     -- res/
                     -- AndroidManifest.xml
                     -- project.properties
             -- build.gradle
             -- gradle.properties
             -- proguard-rules.pro
         -- ijkplayer-x86/  - ijkplayer源码
         -- ijkplayer-x86_64/  - ijkplayer源码
         -- ijkplayer-example/
         -- ijkplayer-exo/
         -- ijkplayer-java/
         -- tools/
         -- build.gradle
         -- gradle.properties
         -- gradlew
         -- gradlew.bat
         -- settings.gradle
     -- patch/
     -- android-ndk-prof
     -- build-on-travis.sh
     -- compile-ijk.sh
     -- ijk-addr2line.sh
     -- ijk-ndk-stack.sh
     -- patch-debugging-with-lldb.sh
 -- config/
 -- doc/
 -- extra/
 -- ijkmedia/
     -- libj4a/  - 编译出静态库libj4a.a,为编译其他库所用
     -- libmediaplayer/  - 编译出共享库libijkplayer.so
     -- libsdl/  - 编译出共享库libijksdl.so
     -- libsoundtouch/  - 编译出静态库libsoundtouch.a,为编译其他库所用
     -- libyuv/  - 编译出静态库libyuv.a,为编译其他库所用
 -- ijkprof/
 -- ios/  - iOS平台上的上层接口封装以及平台相关方法
 -- tools/  - 初始化项目工程脚本
 -- init-android.sh  - 启动初始化脚本
 -- init-android-exo.sh
 -- init-android-libsoxr.sh
 -- init-android-libyuv.sh  - 启动克隆libyuv脚本
 -- init-android-openssl.sh
 -- init-android-prof.sh
 -- init-android-soundtouch.sh  - 启动克隆soundtouch脚本
 -- init-config.sh
 -- init-ios.sh
 -- init-ios-openssl.sh

编译输出:
ijkplayer/android/ijkplayer/ijkplayer-xxxxx/src/main/libs/armeabi-xxxxx/
libijkffmpeg.so libijkplayer.so libijksdl.so

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值