项目中如果要实现视频播放,最方便的就是使用B站开源的ijkplayer。本篇文章就是告知怎样编译,且对编译脚本进行详细分析。
Mac下编译ijkplayer
编译前安装必要软件
1
2
3
4
5
6安装 homebrew
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
安装git
brew install git
安装yasm
brew install yasm配置SDK和NDK路径
1
2
3
4
5把路径的声明放到~/.bash_profile 或者 ~/.profile,我的使用zshrc,所以是放在~/.zshrc
export ANDROID_SDK=<your sdk path>
export ANDROID_NDK=<your ndk path>
设置完成后需要运行
source ./.zshrc如果只是编译ijkplayer的时候使用,那么可以在终端设置临时环境变量
1
2
3
4
5
6
7
8这里要使用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下载ffmpeg
1
2
3
4
5
6
7
8检出项目
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选择配置文件
1
2
3
4
5
6If 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编译ffmpeg
1
2
3
4
5
6cd android/contrib
清除ffmpeg的编译文件
如果出错,看下面错误分析
./compile-ffmpeg.sh clean
编译ffmpeg的所有架构,如果只需要编译armv7a,那么就把all改为armv7a
./compile-ffmpeg.sh all编译完成后,在contrib/build/文件夹下会生成不同架构的文件夹,架构文件夹中的output目录下可以看到生成的libijkffmpeg.so文件,如下图
编译ijkplayer
1
2cd ..
./compile-ijk.sh all编译完成后,在android/ijkplayer文件夹的架构项目库中,可以看到生成的libijkplayer.so、libijkffmpeg.so、libijksdl.so这三个文件被放在项目的
/src/main/libs/架构名/
下了。
sh脚本分析
./init-android.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29截取部分代码
执行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.shcompile-ffmpeg.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42判断传递进来的参数
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24代码顺序进行调整省略
判断不同的平台架构,然后进入"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看到,这是一个环境检查的脚本
1
2
3
4
5
6
7
8
9可以看到,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可以看到如下声明
1
2
3
4
5
6
7
8
9
10如果定义了CONFIG_LINUX_PERF,那么就需要使用到perf_event.h
if CONFIG_LINUX_PERF
ifndef _GNU_SOURCE
define _GNU_SOURCE
endif
include <unistd.h> // read(3)
include <sys/ioctl.h>
include <asm/unistd.h>
include <linux/perf_event.h>
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。