FFMPEG学习第三篇--编译x264并使用x264压缩视频

一:编码介绍

软编码:使用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静态库。


FFMPEG学习第三篇--编译x264并使用x264压缩视频_第1张图片
image.png

三:编译含有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动态库。我们也可以通过在编译过程中打印的日志来查看

FFMPEG学习第三篇--编译x264并使用x264压缩视频_第2张图片
image.png

四:使用包含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

你可能感兴趣的:(FFMPEG学习第三篇--编译x264并使用x264压缩视频)