韦东山嵌入式Linux_3期之USB摄像头监控_手机App增加录像功能(一)
一、手机App功能简述
1) JZ2440开发板作为摄像头监控设备和流服务器,将采集到的摄像头视频数据,通过wifi实时传送到手机App上
2) 手机App作为客户端,通过wifi从流服务器接收摄像头视频,然后显示在屏幕上
3) 显示模式支持:全屏或者保持宽高比
4) 支持:显示FPS帧率
5) 支持拍照功能:把视频的某一帧保存为jpeg图片
6) 支持照片的浏览和删除
7)(新增)支持录像功能:把视频的某一段保存为mkv视频文件
8)(新增)支持录像的浏览和删除
二、概要设计
2.1 技术方案
候选方案有三种:
方案1)在原框架的基础上,编写代码,实现录像功能
优点:可能代码量比较少
缺点:
- 技术难度比较高,实现比较困难
- 后期代码维护工作量比较大。如果拉流要支持另一种视频格式,则还要写代码来实现解码。如果录像要支持另一种视频格式,也要写代码来实现编码。
方案2)原框架的代码保留不变(或仅作少量改动)。仅录像功能,由JNI+FFmpeg来实现并暴露出JNI接口供原框架调用(比如当用户点击“开始录像”后,JAVA层调用JNI层接口)
优点:可能代码量较少。
缺点:
- 技术实现上可能比较困难(比如,怎样实现两者对视频流访问的共享和同步?),可供借鉴的网上资料比较少(目前查到的一种方案是修改ffmpeg源代码,把ffmpeg_cmd.c封装成一个类,由JNI按照形如ffmpeg -i videostream_URL -c:v copy -f avi -y output.avi 命令行参数的方式来调用。但很难实现任意一段视频的保存)
- 后期代码维护工作量比较大。原因同上。
方案3) 修改原框架,把涉及到视频处理的相关代码(比如拉流、解码、播放、存帧,加fps)全部转移到JNI+FFmpeg层
优点:
- 模块边界清晰,耦合度低。Java层只负责UI,而由FFmpeg负责所有视频处理的工作。
- App的扩展性强。由于FFmpeg广泛支持各种视频编解码格式(只需在编译时configure阶段加入对应的配置选项即可),所以手机App也可以很方便的支持显示各种摄像头视频格式,当然录像文件也可支持各种格式。
- 程序可以比较容易的移植到其它平台。FFmpeg支持跨平台(Windows、Linux、MacOS、Android),所以App的移植只需要修改UI相关的代码
- 后期代码维护较容易,由于FFmpeg功能强大,所以预期今后新的需求如果是和视频处理有关的,基本上都可以交给FFmpeg来实现
缺点:
- 修改原框架,实现新框架,涉及的工作量比较大
综合以上分析,决定采用方案3
2.2 开发计划
1) FFmpeg和FreeType的移植
2) App原框架的修改(原框架是基于Android SDK(包括从mjpeg解析出bmp并显示、加fps、拍照等),现要改为由JNI层调用FFmpeg来实现)
实施思路:按功能模块划分,然后逐一实现每个模块,最后再把各个模块整合起来。模块划分如下:
i) (主体模块)视频采集播放
ii) 显示模式切换
iii) 拍照
iv) 录像
v) fps显示
vi) 录像的浏览和删除
三、开发环境
3.1 安卓App开发环境
3.1.1操作系统
Windows10 版本1909
3.1.2 IDE
Android Studio 3.6.1
注:为了防止将原本的Studio直接升级到新版Studio后,导入以前项目时,可能会出现问题,建议下载免安装版本,并且拷贝原Android SDK文件夹为一个新的SDK文件夹,用于新版本的AndroidStudio。参考:AndroidStudio多版本共存(2.3与3.0 )
3.1.3 项目结构(project struct)
1)gradle version
2) SDK location 和 NDK location
注:SDK location 不能包含空格,否则会导致其下的NDK路径也包含空格,从而导致用cmake构建JNI项目时,把空格误认为文件分隔符,而报错
3)Compile SDK version和Build Tools version
4)Target SDK version 和Min SDK version
3.2 ffmpeg和FreeType交叉编译环境
a) Vmware版本:Vmware Workstation 12.5.7
b) Ubuntu版本:Ubuntu 16.04.4 LTS(我用的百问网JZ2440资料光盘_20180516中的版本,地址是:http://wiki.100ask.org/Download,然后找到:002_JZ2440资料光盘_20180516(免费) )
c) 内核版本:4.13.0
d) gcc版本:5.4.0
e) NDK版本:android-ndk-r15c-linux-x86_64.zip
3.3 调试App时用的目标设备
华为畅享9S
四、移植FreeType-2.9.1和FFmpeg-n3.4.5到Android
如果想略过过程,直接使用编译好的库的话,可以到这个地址下载:https://pan.baidu.com/s/1HV-4TB95SPeUqyNDiK68Eg 提取码: 39xb
但在其它机器上没有测试过,不保证一定可用
注:安装NDK交叉编译环境、交叉编译FreeType和FFmpeg都需要在Ubuntu虚拟机中进行
4.1 下载、安装NDK交叉编译环境
(假设下载到/work文件夹下)
下载地址:https://dl.google.com/android/repository/android-ndk-r15c-linux-x86_64.zip?hl=zh_cn
下载完成后执行unzip android-ndk-r15c-linux-x86_64.zip解压即可
4.2 安装NDK standalone toolchain
参考:Android NDK Cross-Compile Setup (libpng and freetype)
4.2.1 安装armv7 版本的NDK standalone toolchain(假设安装到/opt/android-ext文件夹下)
在shell终端里执行以下命令:
export PLATFORM_PREFIX=/opt/android-ext/
export NDK_PATH=/work/android-ndk-r15c/
export NDK_PLATFORM=android-21
mkdir $PLATFORM_PREFIX
$NDK_PATH/build/tools/make-standalone-toolchain.sh --platform=$NDK_PLATFORM --install-dir=$PLATFORM_PREFIX
4.2.2 安装arm64版本的NDK standalone toolchain(假设安装到/opt/android-ext-arm64文件夹下)
(步骤类似于安装arm7v版本的NDK standalone toolchain)
在shell终端里执行以下命令:
export PLATFORM_PREFIX=/opt/android-ext-arm64/
export NDK_PATH=/work/android-ndk-r15c/
export NDK_PLATFORM=android-21
mkdir $PLATFORM_PREFIX
$NDK_PATH/build/tools/make-standalone-toolchain.sh --platform=$NDK_PLATFORM --install-dir=$PLATFORM_PREFIX --arch=arm64
4.3 移植FreeType-2.9.1
4.3.1下载解压FreeType-2.9.1(假设解压到/work/freetype-2.9.1文件夹下)
下载地址:https://download.savannah.gnu.org/releases/freetype/freetype-2.9.1.tar.gz
下载完成后执行tar –xzvf freetype-2.9.1.tar.gz解压即可
4.3.2 交叉编译FreeType-2.9.1
1) 进入/work/freetype-2.9.1文件夹,创建build_freetype.sh脚本文件,内容如下:
#!/bin/bash
NDK=/work/android-ndk-r15c
function configure
{
CPU=$1
PREFIX=$(pwd)/android/$CPU
TOOLCHAIN=""
SYSROOT=""
HOST=""
if [ "$CPU" == "armv7-a" ]
then
TOOLCHAIN=/opt/android-ext/bin/
SYSROOT=$NDK/platforms/android-21/arch-arm/
HOST=arm-linux-androideabi
else
TOOLCHAIN=/opt/android-ext-arm64/bin/
SYSROOT=$NDK/platforms/android-21/arch-arm64/
HOST=aarch64-linux-android
fi
export PATH=$TOOLCHAIN:$PATH
./configure \
--with-png=no \
--with-zlib=no \
--host=$HOST \
--prefix=$PREFIX \
--with-sysroot=$SYSROOT
}
build()
{
make clean
cpu=$1
echo "build $cpu"
configure $cpu
echo "configure done. CFLAGS:$CFLAGS\n PATH:$PATH"
echo "start make..."
#mod on 2020-03-30 by zg
# make -j4
make -j2
echo "make done."
echo "start make install..."
make install
echo "make install done."
}
build arm64
build armv7-a
2) 在shell终端执行以下语句
chmod –R 777 build_freetype.sh
./build_freetype.sh
最终编译成功后,会:
- 在/work/freetype-2.9.1/android/arm64/lib中生成arm64版本的libfreetype.a静态库和libfreetype.so动态库
- 在/work/freetype-2.9.1/android/armv7-a/lib中生成armv7版本的libfreetype.a静态库和libfreetype.so动态库
4.4 移植FFmpeg到android
参考:FFmpeg一键编译Android arm64位和32位共享库
4.4.1 下载解压FFmpeg-n3.4.5(假设解压到/work/FFmpeg-n3.4.5文件夹下)
下载地址:https://github.com/FFmpeg/FFmpeg/archive/n3.4.5.tar.gz
下载完成后执行tar –xzvf FFmpeg-n3.4.5.tar.gz解压即可
4.4.2 下载解压X264
下载地址:https://code.videolan.org/videolan/x264/-/archive/stable/x264-stable.tar.gz
将x264下载到FFmpeg源码根目录,执行tar -zxvf x264-stable.tar.gz解压,然后将解压出来的目录x264-stable重命名为x264
4.4.3 交叉编译X264
1)进入x264源码目录,新建build.sh,内容如下:
#!/bin/bash NDK=/work/android-ndk-r15c configure() { CPU=$1 PREFIX=$(pwd)/android/$CPU HOST="" CROSS_PREFIX="" SYSROOT="" if [ "$CPU" == "armv7-a" ] then HOST=arm-linux SYSROOT=$NDK/platforms/android-26/arch-arm/ CROSS_PREFIX=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi- else HOST=aarch64-linux SYSROOT=$NDK/platforms/android-26/arch-arm64/ CROSS_PREFIX=$NDK/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android- fi ./configure \ --prefix=$PREFIX \ --host=$HOST \ --enable-pic \ --enable-static \ --enable-neon \ --extra-cflags="-fPIE -pie" \ --extra-ldflags="-fPIE -pie" \ --cross-prefix=$CROSS_PREFIX \ --sysroot=$SYSROOT } build() { make clean
cpu=$1
echo "build $cpu"
configure $cpu #-j<CPU核心数> make -j2 make install } build arm64 build armv7-a
2) 在shell终端执行以下语句
chmod –R 777 build.sh
./build.sh
编译成功后,会分别在android/armv7-a/lib和android/arm64/lib目录下找到编译好的静态库libx264.a。
4.4.4 交叉编译FFmpeg-n3.4.5
1)修改configure
进入FFmpeg源码根目录,用vim打开configure,找到
SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'
SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR)$(SLIBNAME)'
将其修改为
SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'
SLIB_INSTALL_LINKS='$(SLIBNAME)'
2)进入FFmpeg源码根目录,新建build_ffmpeg.sh,内容如下:
#!/bin/bash
NDK=/work/android-ndk-r15c ADDI_CFLAGS="-fPIE -pie" ADDI_LDFLAGS="-fPIE -pie" export TMPDIR="/tmp" configure() { CPU=$1 PREFIX=$(pwd)/android/$CPU x264=$(pwd)/x264/android/$CPU HOST="" CROSS_PREFIX="" SYSROOT="" ARCH="" FREETYPE_INCLUDE=/work/freetype-2.9.1/android/$CPU/include FREETYPE_LIB=/work/freetype-2.9.1/android/$CPU/lib if [ "$CPU" == "armv7-a" ] then echo "build $cpu" ARCH="arm" HOST=arm-linux SYSROOT=$NDK/platforms/android-21/arch-arm/ CROSS_PREFIX=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi- else ARCH="aarch64" HOST=aarch64-linux SYSROOT=$NDK/platforms/android-21/arch-arm64/ CROSS_PREFIX=$NDK/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android- fi ./configure \ --prefix=$PREFIX \ --disable-encoders \ --disable-decoders \ --enable-avdevice \ --disable-static \ --disable-doc \ --disable-ffplay \ --enable-network \ --disable-doc \ --disable-symver \ --enable-neon \ --enable-shared \ --enable-libx264 \ --enable-gpl \ --enable-pic \ --enable-jni \ --enable-pthreads \ --enable-mediacodec \ --enable-encoder=aac \ --enable-encoder=gif \ --enable-encoder=libopenjpeg \ --enable-encoder=libmp3lame \ --enable-encoder=libwavpack \ --enable-encoder=libx264 \ --enable-encoder=mpeg4 \ --enable-encoder=pcm_s16le \ --enable-encoder=png \ --enable-encoder=srt \ --enable-encoder=subrip \ --enable-encoder=yuv4 \ --enable-encoder=text \ --enable-encoder=mjpeg \ --enable-decoder=aac \ --enable-decoder=aac_latm \ --enable-decoder=libopenjpeg \ --enable-decoder=mp3 \ --enable-decoder=mpeg4_mediacodec \ --enable-decoder=mjpeg \ --enable-decoder=pcm_s16le \ --enable-decoder=flac \ --enable-decoder=flv \ --enable-decoder=gif \ --enable-decoder=png \ --enable-decoder=srt \ --enable-decoder=xsub \ --enable-decoder=yuv4 \ --enable-decoder=vp8_mediacodec \ --enable-decoder=h264_mediacodec \ --enable-decoder=hevc_mediacodec \ --enable-hwaccel=h264_mediacodec \ --enable-hwaccel=mpeg4_mediacodec \ --enable-ffmpeg \ --enable-bsf=aac_adtstoasc \ --enable-bsf=h264_mp4toannexb \ --enable-bsf=hevc_mp4toannexb \ --enable-bsf=mpeg4_unpack_bframes \ --enable-libfreetype \ --enable-cross-compile \ --cross-prefix=$CROSS_PREFIX \ --target-os=android \ --arch=$ARCH \ --sysroot=$SYSROOT \ --extra-cflags="-I$x264/include -I$FREETYPE_INCLUDE $ADDI_CFLAGS" \ --extra-ldflags="-L$x264/lib -L$NDK/platforms/android-21/arch-$CPU/usr/lib -L$FREETYPE_LIB -llog -lfreetype" } build() { make clean cpu=$1 echo "build $cpu" configure $cpu #mod on 2020-03-30 by zg # make -j4 make -j2 make install } build arm64 build armv7-a
3) 在shell终端执行以下语句
chmod –R 777 build_ffmpeg.sh
./build_ffmpeg.sh
编译成功后,会分别在android/armv7-a/lib和android/arm64/lib目录下找到编译好的动态库:
五、参考资料
1)韦东山嵌入式linux培训3期项目实战之usb摄像头监控
2) NDK 交叉编译环境
Android NDK Cross-Compile Setup (libpng and freetype)
3) 移植FreeType
4) 移植 FFmpeg