参考大神的帖子,亲测一次编译成功:https://blog.csdn.net/bobcat_kay/article/details/80889398
鉴于以前查文档的经验,这里附上编写例子的时间:2018年7月22日
我用的是ubantu,注意事项:
1、路径这里,home/ndk是不对的,真实路径是home/电脑名/ndk,具体以cd ls命令的为基准
2、文件必须要在ubantu下载,我第一次是在win下下载,然后通过VMware传过去的,结果出问题了,后来百度了一下发现了问题,就果断直接ubantu直接下载了一个,然后就可以了
3、build.sh和confinger这两个文件编辑的时候,一定要确保文本的格式不是doc而是unit(单词应该是错了的)格式
大神的帖子用的是mk的,而我这里比较喜欢用cmake。
流程基本一致,但有些小细节,可能大神功力比较深,没遇到啥问题,我是在这个步骤卡了大半个晚上。
附上项目地址:https://gitee.com/imxiaoyu_admin/CmdForFFmpeg4.0.2.git
Android Studio 3.2
NDK 17.1.4828580
SDK 28
看截图吧,主要就是勾上这个东西,其他都默认就好。
都是按照默认目录了,我在cpp目录下新建了个文件夹ffmpeg/armeabi-v7a,将so包都复制进来了
ffmpeg文件夹下新建文件夹include,然后将文件夹fftools里面的部分文件复制到include文件夹中,并且在源代码最外层将config.h复制进来
本着宁杀错不放过的原则,我将源码根目录下的所有文件夹(除了fftools)都复制到了include里面。(参考大神的做法,发现好多文件都少了头文件,然后一直报错编译不过)
创建FFmpegCmd类
看上图,直接alt+enter就可以创建相关的方法了,Android Studio非常智能
然后将c类重命名为ffmpeg_cmd_utils.c
之所以是c而不是cpp,我也很无奈,如果是cpp的话,编译的时候爆了很多警告,并且是不给编译,目前也没有找到到底是什么问题,毕竟我水平也是有限。
主要的几个地方都标出来了
主要就是将各个so包加载进来,还有就是ffmpeg_cmd_utils.c这个类,以及fftools文件夹里面的ffmpeg.c等类(并不需要用到全部,如果全部加载进来反而会报一些莫名其妙的错误,具体要哪些就看cmake文件吧)
如果我没记错步骤的话,到这一步基本上可以编译通过了,下一步开始一直cmd工具
cmake_minimum_required(VERSION 3.4.1)
add_library( # Sets the name of the library.
ffmpegcmd
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/ffmpeg/include/cmdutils.c
src/main/cpp/ffmpeg/include/ffmpeg.c
src/main/cpp/ffmpeg/include/ffmpeg_filter.c
src/main/cpp/ffmpeg/include/ffmpeg_hw.c
src/main/cpp/ffmpeg/include/ffmpeg_opt.c
src/main/cpp/ffmpeg_cmd_utils.c
)
#添加libavcodec-57.so
add_library( avcodec
SHARED
IMPORTED)
set_target_properties( avcodec
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/cpp/ffmpeg/armeabi-v7a/libavcodec.so)
#添加libavdevice-57.so
add_library( avdevice
SHARED
IMPORTED)
set_target_properties( avdevice
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/cpp/ffmpeg/armeabi-v7a/libavdevice.so)
add_library( avfilter
SHARED
IMPORTED)
set_target_properties( avfilter
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/cpp/ffmpeg/armeabi-v7a/libavfilter.so)
add_library( avformat
SHARED
IMPORTED)
set_target_properties( avformat
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/cpp/ffmpeg/armeabi-v7a/libavformat.so)
add_library( avutil
SHARED
IMPORTED)
set_target_properties( avutil
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/cpp/ffmpeg/armeabi-v7a/libavutil.so)
add_library( swresample
SHARED
IMPORTED)
set_target_properties( swresample
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/cpp/ffmpeg/armeabi-v7a/libswresample.so)
add_library( swscale
SHARED
IMPORTED)
set_target_properties( swscale
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/cpp/ffmpeg/armeabi-v7a/libswscale.so)
include_directories(src/main/cpp/ffmpeg/include)
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
target_link_libraries( # Specifies the target library.
ffmpegcmd
avcodec
avdevice
avfilter
avformat
avutil
swresample
swscale
# Links the target library to the log library
# included in the NDK.
${log-lib} )
(1)cmdutils.c
注释掉exit_program方法的内容:
void show_help_children(const AVClass *class, int flags);
改成:
void show_help_children(const AVClass *clazz, int flags);
入口就是main函数,改个名字,这里跟着大神用ffmpeg_exec
注释掉ffmpeg_exec函数末尾的一句代码:
找到ffmpeg_cleanup函数,末尾加上:
nb_filtergraphs = 0;
nb_output_files = 0;
nb_output_streams = 0;
nb_input_files = 0;
nb_input_streams = 0;
加上之后如图:
并且将nb_filtergraphs声明那里赋值为0,保险起见。。。
在ffmpeg_exce函数的末尾加上,不然的话,第二次运行命令行的时候,你的手机就会爆炸。
nb_filtergraphs = 0;
progress_avio = NULL;
input_streams = NULL;
nb_input_streams = 0;
input_files = NULL;
nb_input_files = 0;
output_streams = NULL;
nb_output_streams = 0;
output_files = NULL;
nb_output_files = 0;
下面这一步可以按需减免,主要是用于打印命令行运行之后的输出信息:
添加声明:
#include "android/log.h"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG , "ffmpeg.c", __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR , "ffmpeg.c", __VA_ARGS__)
给log_callback_null函数加上内容:
static void log_callback_null(void *ptr, int level, const char *fmt, va_list vl)
{
static int print_prefix = 1;
static int count;
static char prev[1024];
char line[1024];
static int is_atty;
av_log_format_line(ptr, level, fmt, vl, line, sizeof(line), &print_prefix);
strcpy(prev, line);
//sanitize((uint8_t *)line);
if (level <= AV_LOG_WARNING) {
LOGE("输出:%s", line);
} else {
LOGE("输出:%s", line);
}
}
在末尾加上ffmpeg_exec函数的函数声明
直接上调用代码
#include
#include
#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"8858",FORMAT,##__VA_ARGS__);
JNIEXPORT jint JNICALL
Java_com_imxiaoyu_test_FFmpegPlayer_playMyMedia(JNIEnv *env, jobject instance,jint cmdLen,
jobjectArray cmd) {
int argc = (*env)->GetArrayLength(env, cmd);
char *argv[argc];
int i;
for (i = 0; i < argc; i++) {
jstring js = (jstring) (*env)->GetObjectArrayElement(env, cmd, i);
argv[i] = (char*) (*env)->GetStringUTFChars(env, js, 0);
LOGE("命令行argCmd=%s",argv[i]);
}
ffmpeg_exec(argc, argv);
return 1;
}
没记错的话,到这一步就已经ok了
读写手机内存的权限,这里必须吐槽一下,我在手机是小米6,本着偷懒的原则,在设置的应用的详情页面点击权限管理给app权限了之后,居然是不生效的,命令行一运行就崩溃,必须是代码申请权限才有用,我还以为是编译或者是代码的问题,起码在这一步浪费了三四个小时。不确定是MIUI的特有还是Android 8.0的锅,尽量把工作做足,不要偷懒吧。
后来跑起来了的时候,大家应该都了解我当时的心情,有喜有泪,哭笑不得。
申请权限的代码我就不贴上来了。
final String cmd[]=str.split(" ");//
new Thread(){
@Override
public void run() {
PathUtils pathUtils=new PathUtils();
String str="ffmpeg -i "+pathUtils.getMusicCacheEditorPath()+"/12.mp3 -y "+pathUtils.getMusicCacheEditorPath()+"/haha1.wav";//具体路径,自由发挥吧
long startTime = System.currentTimeMillis();
try {
fFmpegPlayer.playMyMedia(cmd.length,cmd);
}catch (Exception e){
Log.e("处错误了:",e.toString());
}
Log.e("FFmpegTest", "run: 耗时:"+(System.currentTimeMillis()-startTime));
}
}.start();
命令行的所有文件的名字,不要带特殊字符,如空格、换行符、emoji等。
总觉得我还漏了什么没说的,不管了,遇到问题了,记住,猥琐发育,别浪,问题总会解决的。
说真的,我从ffmpeg 3.0的时候就对这件事情很感兴趣了,但是一直卡在编译so包这一步了,对linux系统不熟悉,一直不得要领,然后这一次是照着大神的脚步,一两次就直接编译出来so包了,我就知道这次是有戏了。
ffmpeg的命令行工具其实已经可以做不少比较好玩的事情了,预祝大家玩的开心。