编译FFmpeg并移植到Android中使用实战详解教程
1.搭建编译环境
1.安装ubuntu14.04,安装完成后执行以下命令
apt-get update
apt-get install yasm
apt-get install pkg-config
2.下载ndk
这里用最新稳定版ndk r19c:下载ndk-r19c
将ndk下载到 /home/ndk/目录下,下载完成后执行unzip android-ndk-r19c-linux-x86_64.zip解压
3.下载FFmpeg4.1.3
下载FFmpeg-n4.1.3
下载完成后执行tar -zxvf n4.1.3.tar.gz解压
2.编译FFmpeg
1.修改configure
进入源码根目录,用vim打开configure,找到
将其修改为
2.配置编译脚本
在源码根目录新建build.sh,内容如下:
*注意 x264需要自己编译引入,ffmpeg并不包含x264
可以根据自己的需求对模块进行裁剪,
查看所有编译配置选项:./configure --help
查看支持的解码器:./configure --list-decoders
查看支持的编码器:./configure --list-encoders
查看支持的硬件加速:./configure --list-hwaccels
赋予脚本执行权限:chmod +x build.sh
执行脚本开始编译:./build.sh
如果一切顺利就可以在源码更目录下的android/armv7-a/lib/下找到我们需要的.so文件了
3.移植到Android App中
1.JNI编译
(1)将编译后的FFmpeg源码目录打包并复制到Windows中,在任意目录下新建jni文件夹,将 android\armv7-a\lib 目录下的.so文件复制到 jni\prebuilt\ 目录下;
将 android\armv7-a\include 目录下的所有文件夹复制到 jni 目录下;
将 源码根目录下的config.h复制到 jni 目录下;
将 fftools\ 目录下的以下文件复制到 jni 目录下:
cmdutils.h
ffmpeg.h
ffmpeg.c
ffmpeg_opt.c
ffmpeg_filter.c
cmdutils.c
ffmpeg_hw.c
(2)修改cmdutils
打开cmdutils.h,将
void show_help_children(const AVClass *class, int flags);
改为
void show_help_children(const AVClass *clazz, int flags);
否则和C++一起编译会出问题
(3)修改ffmpeg.c
找到入口函数int main(int argc, char **argv)
将其修改为int ffmpeg_exec(int argc, char **argv)
将此函数中所有调用exit_program()的地方都注释掉,并在函数末尾添加以下代码重置状态:
同时在ffmpeg.h中添加函数申明:int ffmpeg_exec(int argc, char **argv);
(4)输出ADB日志
在ffmpeg.c中引入头文件
实现log_callback_null
函数
(5)实现JNI接口
编写ffmpeg-invoke.cpp并放到 jni 目录下:
注意:将com_github_ffmpegtest_jni_FFmpegCmd改为自己工程中的JAVA文件对应的包名
(6)编写.mk文件
编写Android.mk文件并放到 jni 目录
注意:将LOCAL_C_INCLUDES改为自己电脑中FFmpeg源码所在目录
编写Application.mk并放到 …\jni\目录下
(7)编译
确认所需文件都准备就绪:
打开CMD,进入该目录,执行ndk-build开始编译(确保ndk路径已经配置到环境变量中)
编译过程中可能出现的报错:
1.error: invalid suffix on literal; C++11 requires a space between literal and identifier [-Wreserved-user-defined-literal] snprintf(name, sizeof(name), "0x%"PRIx64, ch_layout);
解决方法:在cmdutils.h中找到 "0x%"PRIx64,在PRIx64前面加个空格即可。
2.error: assigning to 'BenchmarkTimeStamps' (aka 'struct BenchmarkTimeStamps') from incompatible type 'int64_t' (aka 'long long') current_time = ti = getutime();
解决方法:在ffmpeg.c中找到current_time = ti = getutime();,将这行代码改为current_time.sys_usec = ti = getutime();
3.error: undefined reference to 'postproc_version'
解决方法:在cmdutils.c中注释这行代码
PRINT_LIB_INFO(postproc, POSTPROC, flags, level);
4.error: undefined reference to 'getutime'
解决方法:在ffmpeg.c中添加下面这个函数:
编译成功可以看到生成的.so文件:
在jni同级目录下找到libs目录,libs\armeabi-v7a目录下的.so文件就是我们最终需要的动态链接库:
2.新建Android Studio测试工程 FFmpegTest
(1)在FFmpegTest\app\src\main\目录下新建jniLibs目录,将上一步编译生成的 \libs\armeabi-v7a文件夹复制到jniLibs目录下;
在com.github.ffmpegtest.jni路径下新建FFmpegCmd.java
public class FFmpegCmd {
static {
System.loadLibrary("avutil");
System.loadLibrary("avcodec");
System.loadLibrary("swresample");
System.loadLibrary("avformat");
System.loadLibrary("swscale");
System.loadLibrary("avfilter");
System.loadLibrary("ffmpeg-invoke");
}
private static native int run(int cmdLen, String[] cmd);
public static native String test();
public static int run(String[] cmd){
return run(cmd.length,cmd);
}
}
(2)测试FFmpeg命令
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
100);
}
TextView tvMessage = findViewById(R.id.tv_message);
tvMessage.setText(FFmpegInvoke.test());
ffmpegTest();
}
private void ffmpegTest() {
new Thread(){
@Override
public void run() {
long startTime = System.currentTimeMillis();
String input = "/sdcard/Movies/Replay_2018.05.08-13.46.mp4";
String output = "/sdcard/Movies/output.mp4";
//剪切视频从00:20-00:28的片段
String cmd = "ffmpeg -d -ss 00:00:20 -t 00:00:08 -i %s -vcodec copy -acodec copy %s";
cmd = String.format(cmd,input,output);
FFmpegCmd.run(cmd.split(" "));
Log.d("FFmpegTest", "run: 耗时:"+(System.currentTimeMillis()-startTime));
}
}.start();
}
}
执行完毕后再/sdcard/Movies/目录下找到output.mp4,打开播放确实是我们想要的结果,说明FFmpeg的命令成功执行了。
评论