欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-07-13 15:38:29
...

一:编码介绍

软编码:使用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压缩视频

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压缩视频

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来提取时间了

 

相关标签: video