一:编码介绍
软编码:使用cpu进行编码。
硬编码:使用显卡GPU专用的DSP\FPGA\ASIC芯片等硬件进行编码。
软编码的特点是升级方便,实现简单,但是cpu负载中,性能低。硬编码性能高。
在H.265之前,H.264一直是新一代的编码标准。H265主要是用来满足4
k,8k视频的需求,H.264以高压缩高质量和支持多种网络流媒体传输而闻名。经过h264标准对视频进行压缩编码,能够有效减小视频的文件大小,这主要是受益于他的低码率。与MPEG-2和MPEG-4 ASP等压缩技术相比,H.264压缩技术节省来用户的下载时间和流量。
H.264只是一个标准,具体实现包括JM,T264,x264。而由于种种原因,JM,T264等实现工具,并不能很好地继续完成我们的需求,因此x264便成了互联网世界中主流的实现H.264标准的编码工具。
FFMPEG默认只支持对H.264的解码,并不支持对H.264的编码,因此就需要我们手动配置FFMPEG来实现对H.264的编码。首先我们需要下载x264
二:编译x264
下载完成之后,我们需要首先编译x264为静态库,这是因为FFMPEG默认是通过静态链接方式来链接其他功能库。我们在编译ffmpeg时,修改了configure文件,这样方便生成可以让android识别的*.so文件,但是我们编译x264是生成的静态库,因此不需要修改x264的configure文件。下面我们直接放一个网上的编译脚本,build_x264_arm.sh:
#!/bin/bash
NDK=/Users/xiaguangcheng/android/android-sdk-macosx/ndk-bundle
SYSROOT=$NDK/platforms/android-18/arch-arm/
TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64
CPU=arm
PREFIX=/Users/xiaguangcheng/x264/arm
ADDI_CFLAGS=""
ADDI_LDFLAGS=""
function build_x264
{
./configure \
--prefix=$PREFIX \
--disable-shared \
--disable-asm \
--enable-static \
--enable-pic \
--enable-strip \
--host=arm-linux-androideabi \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--sysroot=$SYSROOT
--extra-cflags="-Os -fpic $ADDI_CFLAGS $OPTIMIZE_CFLAGS" \
--extra-ldflags="$ADDI_LDFLAGS" \
$ADDITIONAL_CONFIGURE_FLAG
make clean
make -j4
make install
}
build_x264
下面的几行需要根据你的电脑来完成配置。同时在x264文件夹下新建一个arm的文件夹用来存放编译之后的文件。
NDK=/Users/xiaguangcheng/android/android-sdk-macosx/ndk-bundle
SYSROOT=$NDK/platforms/android-18/arch-arm/
TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64
CPU=arm
PREFIX=/Users/xiaguangcheng/x264/arm
接着我们通过命令cd x264进入到x264文件夹内,查看新建的build_x264_arm.sh文件的权限
ls -l build_x264_arm.sh
-rwxrwxrwx 1 xiaguangcheng staff 733 6 26 20:46 build_x264_arm.sh
如果你的该文件不具备可执行权限,那么就需要手动添加可执行权限,添加完权限之后,记得再查看一下是否成功。
chmod 777 build_x264_arm.sh
ls -l build_x264_arm.sh
如果还没有安装yasm那么可以通过homebrew安装
brew install yasm
否则可能会报一个错误
if you really want to compile without asm, configure with --disable-asm. make: *** [config.mak] Error 1 Makefile:3: config.mak: No such file or directory
接下来我们在命令行中就可以运行该文件进行编译
./build_x264_arm.sh
待编译结束后,我们在刚刚创建的arm文件夹中的就可以找到一个.a静态库。
三:编译含有x264静态库的ffmpeg动态库
在第一篇中我们讲解来如何编译一个ffmpeg动态库,但是那个动态库并没有包含x264,这里我们将x264包含进来。因此在编译ffmpeg动态库的编译文件基础之上进行略微的修改即可,buildffmpeg.sh文件内容更新如下:
#!/bin/bash
export TMPDIR=/Users/xiaguangcheng/ffmpeg/ffmpeg/ffmpegtemp
NDK=/Users/xiaguangcheng/android/android-sdk-macosx/ndk-bundle
SYSROOT=$NDK/platforms/android-18/arch-arm/
TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64
CPU=arm
PREFIX=/Users/xiaguangcheng/ffmpeg/ffmpeg/android
ADDI_CFLAGS="-marm"
function build_one
{
./configure \
--prefix=$PREFIX \
--enable-shared \
--enable-gpl \
--disable-static \
--disable-doc \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-doc \
--disable-symver \
--enable-small \
--enable-gpl \
--enable-libx264 \
--enable-yasm \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--target-os=linux \
--arch=arm \
--enable-cross-compile \
--sysroot=$SYSROOT \
--extra-cflags="-I/Users/xiaguangcheng/x264/arm/include" \
--extra-ldflags="-L/Users/xiaguangcheng/x264/arm/lib" \
$ADDITIONAL_CONFIGURE_FLAG
make clean
make
make install
}
build_one
主要修改包括以下几点
--enable-gpl \
--enable-libx264 \
--enable-yasm \
--extra-cflags="-I/Users/xiaguangcheng/x264/arm/include" \
--extra-ldflags="-L/Users/xiaguangcheng/x264/arm/lib" \
- -I:编译阶段生效的参数,用于指定头文件的搜索路径。
- -L:链接阶段生效的参数,用于指定链接库的搜索路径,-l用于指定链接库的名称,一般两者一起使用的话,就可以指定动态链接库。
查看该文件是否有执行权限,如果有执行权限
./buildffmpeg.sh
运行该命令后即可得到最终的包含x264的ffmpeg的so动态库。我们也可以通过在编译过程中打印的日志来查看
四:使用包含x264的ffmpeg动态库在android中压缩视频
在android中使用ffmpeg执行命令,并通过命令来编码压缩视频,利用FFmpeg玩转Android视频录制与压缩(一),这篇博客已经说的差不多了,但是根据我们项目中上传视频的需求,我们还需要获取在编码压缩过程中的进度,具体思路是通过process来执行命令,而不是通过jni调用ffmpeg来执行命令。而上面提到的这篇博客就是通过jni调用ffmpeg来执行命令的。通过android.jar中的Process来执行命令,可以获取到执行命令的结果,由于在执行ffmpeg的过程中,会出现诸如下面这样的结果输出
frame= 125 fps=0.0 q=38.0 size= 176kB time=00:00:08.54 bitrate= 168.7kbits/frame= 298 fps=297 q=35.0 size= 471kB time=00:00:20.03 bitrate= 192.6kbits/frame= 454 fps=302 q=35.0 size= 727kB time=00:00:30.46 bitrate= 195.4kbits/frame= 662 fps=331 q=33.0 size= 1067kB time=00:00:44.37 bitrate= 197.0kbits/frame= 853 fps=341 q=36.0 size= 1386kB time=00:00:57.05 bitrate= 199.0kbits/frame= 1041 fps=347 q=37.0 size= 1719kB time=00:01:09.56 bitrate= 202.4kbits/frame= 1241 fps=354 q=37.0 size= 2063kB time=00:01:22.91 bitrate= 203.8kbits/frame= 1478 fps=369 q=37.0 size= 2475kB time=00:01:38.73 bitrate= 205.3kbits/frame= 1677 fps=372 q=38.0 size= 2815kB time=00:01:51.96 bitrate= 206.0kbits/frame= 1889 fps=378 q=38.0 size= 3181kB time=00:02:06.13 bitrate= 206.6kbits/frame= 2101 fps=382 q=36.0 size= 3535kB time=00:02:20.29 bitrate= 206.4kbits/frame= 2341 fps=390 q=32.0 size= 3931kB time=00:02:36.22 bitrate= 206.1kbits/frame= 2602 fps=400 q=36.0 size= 4393kB time=00:02:53.63 bitrate= 207.3kbits/frame= 2809 fps=401 q=27.0 size= 4728kB time=00:03:07.47 bitrate= 206.6kbits/frame= 3016 fps=406 q=-1.0 Lsize= 5098kB time=00:03:21.01 bitrate= 207.7kbits/s speed= 27x
那么我们就可以通过先获取本地视频的时长,然后再拿到输出结果中的转换时长来查看编码进度。上面输出中的size并不是原视频的size,而是编码之后的新视频已经生成的size,因此不可以参考。但是我们编码只是压缩了大小,并没有改变时长,因此时间可以用来参考
1:获取本地视频时长
Cursor mCursor = mContentResolver.query(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
null, null, null,
MediaStore.MediaColumns.DATE_ADDED + " DESC");
int columnIndex1 = mCursor.getColumnIndex(MediaStore.Video.Media.DURATION);
long millSeconds = mCursor.getLong(columnIndex1);
2:获取ffmpeg命令输出结果
String [] cmd =new String []{"ffmpeg","-i","xiaugangcheng.mp4","xia.mp4"};
Process process = new ProcessBuilder()
.command(cmd)
.redirectErrorStream(true)
.start();
try {
InputStream in = process.getInputStream();
OutputStream out = process.getOutputStream();
readStream(in);
} finally {
process.destroy();
}
3:根据输出结果获取已转换的视频时长
String s=""frame=125 fps=0.0 q=38.0 size=176kB time=01:11:08.54 bitrate= 168.7kbits"";
int index = s.indexOf("time=");
if(index==-1){
return;
}
if(index+5>s.length()||index+13>s.length()){
return;
}
String cmd = s.substring(index+5, index+13);
System.out.println(cmd);
String[] my =cmd.split(":");
int hour =Integer.parseInt(my[0]);
int min =Integer.parseInt(my[1]);
int sec =Integer.parseInt(my[2]);
int totalSec =hour*3600+min*60+sec;
int percent =String totalSec *100/ 202;
progressDialog.setMessage("Processing\n"+percent+"%");
拿到输入输出流,就可以转化为string来提取时间了
文章参考
初识FFmpeg编译那些事
Mac下为Android编译FFMPEG和x264
在Android上使用FFmpeg压缩视频
编译Android下可执行命令的FFmpeg