type
status
date
slug
summary
tags
category
icon
一、下载源码
二、编译 FFmpeg
编译 x264 编码器
先在
build
文件夹下建立 setting.sh
, 用于申明一些公用的环境变量,比如 $NDK
、$CPU
...然后建立
libx264
的编译脚本 build_x264.sh
,libx264
是一个开源的H.264编码器,据说是最好的视频有损编码器。ffmpeg
默认不自带,但是支持 x264
作为第三方编码器编译。build_x264.sh
./config 内的# 注释必须在运行的时候去掉
写完之后就可以编译
x264
库了,编译之前还有一点要注意的是,默认编译出来的文件后缀并不是 *.so
,这 Android
是识别不了的,需要对 x264
源码里面的 config
做如下修改:将
修改成
别忘了给 build_x264.sh 和 setting.sh 赋予可执行权限 (chmod +x build_x264.sh setting.sh)
修改完后就可以执行脚本命令了
等待一段时间后,
build
文件夹目录下应该有个 lib
目录(build 脚本里面 prefix 指定的目录),里面存放了 x264
的静态库这里为什么编译成静态库而不是动态库呢?静态库可以把内容编译到待会儿要编译 ffmpeg 的so库里去,不需要单独加载 libx264.so 了,如果你硬要编译成动态库也可以,加载 ffmpeg.so 的时候加载 libx264.so 就可以
至此,
x264
编码器编译完毕编译 FFmpeg
同样在
build
文件夹下建立编译脚本 build_ffmpeg.sh
,编译 ffmpeg
比编译 x264
略微麻烦点,首先肯定不能全功能编译,那还不如直接去网上找一个编译好的,要自己定制哪些组件需要,哪些组件不需要FFmpeg它主要含有以下几个核心库:
- libavcodec-提供了更加全面的编解码实现的合集
- libavformat-提供了更加全面的音视频容器格式的封装和解析以及所支持的协议
- libavutil-提供了一些公共函数
- libavfilter-提供音视频的过滤器,如视频加水印、音频变声等
- libavdevice-提供支持众多设备数据的输入与输出,如读取摄像头数据、屏幕录制
- libswresample,libavresample-提供音频的重采样工具
- libswscale-提供对视频图像进行色彩转换、缩放以及像素格式转换,如图像的YUV转换
- libpostproc-多媒体后处理器
如果不修改什么配置,直接编译的话,我发现
libavcodec.so
有 7.8MB,我可以在这方面下手,指定 decoder
和 encoder
,因为我需要的是视频压缩,所以编码器(encoder
)我就只需要 x264
(视频编码) 和 aac
(音频编码),至于解码器,挑几个常用的就可以了查看编码器和解码器种类,可以通过 ./config --list-decoders 或 ./config --list-encoers 命令实现(ffmpeg目录下) ./config 内的# 注释必须在运行的时候去掉
这次编译不用静态库的原因是,静态库链接是有顺序要求的,这里模块太多,我也不知道哪个模块依赖哪个模块,所以直接上动态库
脚本写完后,就可以 run 了,编译时间有点久,可以学学我的某个同学,一编译就起来泡泡妹子,有说有笑。
编译完成后你的目录应该是下面那个样子:
后面的版本号不一样没关系,这由 ffmpeg 版本决定的
库编译完了,这些 so 库就是在 Android 可用的动态库,接下来就可以准备 JNI 编程了
三、在 Android 里使用 FFmpeg
前面已经把
FFmpeg
各个核心库编译出来了,但是我肯定不会在里面直接用核心库内的函数来用,ffmpeg
本来是一个在 pc 端的命令,命令里面可以填写各种参数,比如 ffmpeg -i a.mp4 -c:v x264 -c:a aac b.mp4
,就是把 a.mp4 用 x264
(视频)、aac
(音频) 编码成 b.mp4
。ffmpeg
是由 ffmpeg.c
编译出来的,想要在 Android 里面用 ffmpeg
命令,只要修改 ffmpeg.c
里面的 main 函数,比如修改成 int run_ffmpeg_command(int args, char **argv)
,然后用 JNI 暴露给 java 调用,就可以在 Android 使用 ffmpeg
命令了在 FFmpegAndroid 建立一个 Android 工程,然后新建一个 ffmpeg 的 lib module
对于 NDK 开发,AndroidStudio 2.2 以后就有较好的支持,直接修改支持库的 build.gradle 文件
这样 lib module 就支持 c++ 了,方便吧!比以前的 Android.mk 不知道方便多少
然后在模块的
src/main
下面新建一个 cpp
目录,用于存放 c++ 代码,从ffmpeg
拷贝以下文件:然后在 CMakeList.txt 里面配置这些文件,好让 AndroidStudio 认识它们
刷新下 gradle,就可以写 c++ 代码了。先看下
ffmpeg.c
这个文件,原先的指令其实调用的就是 main 函数,我们先把 main 函数改成自己自定义的函数 run_ffmpeg_command
:改了以后,我们就可以调用
run_ffmpeg_command
然后传入参数,相当于在 pc 执行 ffmpeg
命令。不过现在还不能执行,这是个坑点,仔细看 run_ffmpeg_command
函数,在程序结束的时候,或者中途出现错误的时候,都会调用 exit_program(int)
,这个函数:exit_program(int)
函数是什么,跳过去看一下发现里面就是清理资源然后 exit(int)
,这里就要注意这个 exit 函数了,除非我们是多进程方式调用 run_ffmpeg_command
,如果我们在 app 的进程调用,执行了 exit 就会结束 app 的进程!这不是我想看到的,最好的方法是另开一个进程调用,但是这样就涉及到了进程间的通信问题,麻烦,不想写!反正只是跑一个压缩指令嘛,直接改
ffmpeg.c
,首先把 exit(int)
函数给注释掉,然后返回一个 code,run_ffmpeg_command
函数里面只要涉及到 exit_program(int)
函数调用的地方都写成 return exit_program(int)
,不过要注意,有如下几个坑点:修改 ffmpeg.c 坑点一
调试的时候发现
return exit_program(int);
语句并不会结束当前函数并返回,而是继续往下执行了,当时一脸楞逼,我艹!!这是什么鬼??为什么我 return 了没有用?找了半天后才发现是 exit_program(int)
这个函数声明的锅!看下面这个函数的声明:函数后面有个奇怪的
av_noreturn
声明,网上查了一下才知道,这个是给编译器的注解,这货的锅,去掉就好了。修改 ffmpeg.c 坑点二
其实
exit_program(int)
这个函数不只是在 run_ffmpeg_command
里面调用,其它各种函数里面都有,如果都要修改的话必须一层一层的 return (C语言里面没有异常啊),很麻烦,但是如果没有改好的话就很容易 crash,这是个要解决的问题,首先 run_ffmpeg_command
里面的 exit_program
都要改成 return 方式然后因为最终目的是压缩视频,参数集是固定的,所以不用考虑编码不支持,或参数匹配不到的情况,只需要考虑文件读写的问题,就是输入文件不存在的时候,或者输出路径不合法的时候,不能让程序异常退出,而是返回错误码,这个需要改
ffmpeg_opt.c
这个文件
ffmpeg_opt.c目前我项目中就只改了这几个函数内的
exit_program
,测试可行,也可以参考本项目的代码,链接在文末最后就是暴露
run_ffmpeg_command
方法给 java 调用了,这个和普通的 JNI 编程一样,建一个 native 的方法,创建 cpp 代码。。。没啥东西,直接上代码FFmpegNativeBridge
ffmpeg-lib.c
运行前先需要把
ffmpeg
编译出来的一堆 so
库放到 jniLibs
内,不然运行的时候会出现动态库无法加载的异常。最后就可以在 Android
内用 ffmpeg
的命令了:关于这些参数,可以去查 FFmpeg 的官网,本项目源码地址Github
- Author:NotionNext
- URL:https://tangly1024.com/article/example-8
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!
Relate Posts