前言
前述博文《基于FFmpeg和Android的音视频同步播放实现》中,我们按照自己的方法实现了一个简单的媒体播放器,并做了音视频同步。但是这个程序在播放更多码流时,遇到了一些问题,包括视频解码问题,同步时视频卡顿等。我们在博文《在ubuntu16.04下测试ffplay程序》中也测试过ffplay这个强大的播放器,而我们也知道SDL基于Android平台也有支持,所以我们可以将ffplay和sdl移植到Android平台上来。
主要工作
- 编译FFmpeg的Android平台动态库,以实现视频解码;
- 编译SDL2的Android平台动态库,以支持对视频显示和音频播放的芯片支持;
- 移植ffplay程序到Android平台,以实现一个基本的媒体播放器;
FFmpeg和ffplay都在native层实现或编译,SDL2一部分在native层编译,另一部分平台依赖需要在java层实现,当然,SDL2均提供了参考代码。
源代码版本
FFmpeg基于当前最新的4.0.1版本;
SDL2基于当前最新的SDL2-2.0.8版本;
ffplay也是基于FFmpeg4.0.1附带的源码;
编译libffmpeg.so
FFmpeg库的编译基于android-ndk-r14b在Ubuntu下编译,注意采用standalone_toolchain的编译链,可以参考https://blog.csdn.net/ericbar/article/details/80229592 , ndk采用r14b而没有采用最新的版本,因为从r15b版本开始,FFmpeg代码中引用stderr等标准输入输出函数会找不到。
config.sh和make.sh需要做些修改,分别参考如下,
#!/bin/bash
FFMPEG_SRC_PATH=$(cd `dirname $0`; pwd)
SYSROOT=/home/ffmpeg/work/toolchain/android/linux-x86_64/ndk-r14/android-19/arm/sysroot
LIBPATH=/home/ffmpeg/work/toolchain/android/linux-x86_64/ndk-r14/android-19/arm/sysroot/usr/lib
TOOLCHAIN=/home/ffmpeg/work/toolchain/android/linux-x86_64/ndk-r14/android-19/arm
export PATH=$TOOLCHAIN/bin:$PATH
export CROSS_PREFIX=arm-linux-androideabi-
export CC="${CROSS_PREFIX}gcc "
export CXX=${CROSS_PREFIX}g++
export LD=${CROSS_PREFIX}ld
export AR=${CROSS_PREFIX}ar
export STRIP=${CROSS_PREFIX}strip
LDFLAGS="-lm -lz -Wl,-soname=libffmpeg.so,-z,noexecstack"
PREFIX=ffout
ADDI_CFLAGS="-marm"
echo " "
echo "please wait..."
echo " "
#cd $FFMPEG_SRC_PATH
rm ./$PREFIX -rf
make clean
echo " "
echo "preparing to configure..."
echo " "
./configure \
--prefix=$PREFIX \
--enable-shared \
--disable-static \
--disable-doc \
--disable-ffmpeg \
--disable-ffprobe \
--disable-doc \
--disable-symver \
--disable-programs \
--disable-w32threads \
--disable-os2threads \
--disable-sdl2 \
--tempprefix=$FFMPEG_SRC_PATH \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--target-os=linux \
--arch=arm \
--enable-cross-compile \
--sysroot=$SYSROOT \
--extra-cflags="-Os -fpic $ADDI_CFLAGS" \
--extra-ldflags="$ADDI_LDFLAGS" \
$ADDITIONAL_CONFIGURE_FLAG
# --sysinclude=$SYSROOT \
下面make.sh里,需要把avdevice下的.o库链接添加进来,
#!/bin/bash
FFMPEG_SRC_PATH=$(cd `dirname $0`; pwd)
SYSROOT=/home/ffmpeg/work/toolchain/android/linux-x86_64/ndk-r14/android-19/arm/sysroot
LIBPATH=/home/ffmpeg/work/toolchain/android/linux-x86_64/ndk-r14/android-19/arm/sysroot/usr/lib
TOOLCHAIN=/home/ffmpeg/work/toolchain/android/linux-x86_64/ndk-r14/android-19/arm
export TMPDIR=$FFMPEG_SRC_PATH
export PATH=$TOOLCHAIN/bin:$PATH
export CROSS_PREFIX=arm-linux-androideabi-
export CC="$CCACHE ${CROSS_PREFIX}gcc "
export CXX=${CROSS_PREFIX}g++
export LD=${CROSS_PREFIX}ld
export AR=${CROSS_PREFIX}ar
export STRIP=${CROSS_PREFIX}strip
LDFLAGS="-lm -lz -Wl,-soname=libffmpeg.so,-z,noexecstack"
CPU=arm
PREFIX=ffout
ADDI_CFLAGS="-marm"
#make -j${NUMBER_OF_CORES} && make install || exit 1
make -j4 && make install || exit 1
rm libavcodec/reverse.o libavcodec/log2_tab.o libavformat/log2_tab.o libavformat/golomb_tab.o \
libswresample/log2_tab.o libavfilter/log2_tab.o libswscale/log2_tab.o libavdevice/reverse.o
$CC -o $PREFIX/libffmpeg.so -shared $LDFLAGS $EXTRA_LDFLAGS --sysroot=$SYSROOT -L $LIBPATH \
libavutil/*.o libavutil/arm/*.o libavcodec/*.o libavcodec/arm/*.o \
libavformat/*.o libavfilter/*.o libswresample/*.o libswresample/arm/*.o \
libswscale/*.o libswscale/arm/*.o compat/*.o libavdevice/*.o
cp $PREFIX/libffmpeg.so $PREFIX/libffmpeg-debug.so
${STRIP} --strip-unneeded $PREFIX/libffmpeg.so
编译SDL2库
我们下载SDL2-2.0.8源码后,解压缩,直接进入SDL2-2.0.8目录,首先需要设置NDK的环境变量,这里注意NDK不要采用类似FFmpeg编译的standalone_toolchain方式,采用从Google官网下载的原始版本解压缩即可,版本的话,使用android-ndk-r16b即可,参考下述命令即可启动编译,
export ANDROID_NDK_HOME=/home/ffmpeg/work/android-ndk-r16b
export PATH=$ANDROID_NDK_HOME:$PATH
ndk-build -B NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk
最后在SDL2-2.0.8\libs\armeabi-v7a下会生成libSDL2.so库。
移植ffplay
ffplay程序基于eclipse来编译,其中eclipse的ndk版本采用android-ndk-r13b;主要是一些头文件依赖和编译警告,需要做一些适当的调整和修正。
SDL2的java层适配
SDL2的源码里提供了相关参考,我们直接搬过来就行,代码路径位于,
SDL2-2.0.8\android-project\app\src\main\java\org\libsdl\app
当然,我们需要做一点修改,主要有如下几点:
1. SDLActivity.java中,主程序(main函数)所在的so库名是libfflay.so而不是libmain.so,这个是我在创建工程时选的so名字,所以要修改过来,大家可以根据自己实际名字修改。
2. SDLActivity.java中,加载的so库,由main修改成ffmpeg和ffplay;
3. SDLActivity.java中,native层启动的主程序名由SDL_main改成start,注意我们这里把ffplay源码里的main函数改成了start函数;
GitHub源码
代码修改部分比较琐碎,这里不一一讲述,媒体播放url在ffplay.c的input_filename全局变量初始化时直接赋值。
请参考完整的源码路径:
https://github.com/ericbars/ffplay