ffmpeg 学习二讲了把ffmpeg导入到项目中。今天说一下怎么在android上直接调用ffmpeg的命里,来实现把小视频转换成GIF动图格式。 看这篇之前先把学习一和学习二搞定,这里直接从学习二的基础上添加文件。
1.添加必要文件
1.1这些文件都在上一篇文章添加了
QQ截图20211130163035.png
1.2.下面是添加ffmpeg运行命令的文件,文件在下载的ffmpeg里面的fftools文件夹里面。
QQ截图20211206150459.png
这里面有一个ffmpeg_hw.老版本的可能没有,因为我看别人文章都没有添加这个,但是发现最新版本如果不添加,会出现错误,主要是ffmpeg.h的方法,实现在这个类里面。
1.3.修改ffmpeg.c
mian方法改个名字。
int runCmd(int argc, char **argv)
{
略...最后添加下面代码,恢复状态
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;
return main_return_code;
}
在ffmpeg.h文件里面,找个位置添加
int runCmd(int argc, char **argv);
cmdutils.c修改退出代码,直接屏蔽掉。
void exit_program(int ret)
{
// if (program_exit)
// program_exit(ret);
//
// exit(ret);
}
把native-lib.cpp文件直接改名成ffmpeg_cmd.c,代码如下
#include <jni.h>
#include "ffmpeg.h"
JNIEXPORT jint
JNICALL
Java_com_tj_myapplication_MainActivity_run(
JNIEnv *env, jclass obj, jobjectArray commands) {
int argc = (*env)->GetArrayLength(env, commands);
char *argv[argc];
int i;
for (i = 0; i < argc; i++) {
jstring js = (jstring) (*env)->GetObjectArrayElement(env, commands, i);
argv[i] = (char *) (*env)->GetStringUTFChars(env, js, 0);
}
return runCmd(argc, argv);
}
修改文件CMakeLists.txt,这里呢有个坑,不算大,不算小吧。主要是CMakeLists.txt文件位置导致的路径出现偏差,找不到文件。上一篇文章提到过,正好我这里有配置了一套,就都贴下来了。 1.CMakeLists.txt文件在cpp目录下,配置如下
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.10.2)
project("myapplication")
set(my_lib_path ${CMAKE_SOURCE_DIR}/../../../libs)
add_library(
myapplication
SHARED
cmdutils.c
ffmpeg.c
ffmpeg_filter.c
ffmpeg_opt.c
ffmpeg_cmd.c
ffmpeg_hw.c)
find_library(
log-lib
log)
add_library(libavcodec
SHARED
IMPORTED )
set_target_properties(libavcodec
PROPERTIES IMPORTED_LOCATION
${my_lib_path}/armeabi-v7a/libavcodec.so )
add_library(libavfilter
SHARED
IMPORTED )
set_target_properties(libavfilter
PROPERTIES IMPORTED_LOCATION
${my_lib_path}/armeabi-v7a/libavfilter.so )
add_library(libavformat
SHARED
IMPORTED )
set_target_properties(libavformat
PROPERTIES IMPORTED_LOCATION
${my_lib_path}/armeabi-v7a/libavformat.so )
add_library(libavutil
SHARED
IMPORTED)
set_target_properties(libavutil
PROPERTIES IMPORTED_LOCATION
${my_lib_path}/armeabi-v7a/libavutil.so )
add_library(libswresample
SHARED
IMPORTED)
set_target_properties(libswresample
PROPERTIES IMPORTED_LOCATION
${my_lib_path}/armeabi-v7a/libswresample.so )
add_library(libswscale
SHARED
IMPORTED)
set_target_properties(libswscale
PROPERTIES IMPORTED_LOCATION
${my_lib_path}/armeabi-v7a/libswscale.so )
include_directories( ${my_lib_path}/include)
target_link_libraries( myapplication
libavcodec libavfilter libavformat libavutil libswresample libswscale
${log-lib} )
2.CMakeLists.txt文件在app目录下与src平级配置如下
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.10.2)
# Declares and names the project.
set(CPP_DIR ${CMAKE_SOURCE_DIR}/src/main/cpp)
# 添加库
add_library( # 库名称
myapplication
# 动态库,生成so文件
SHARED
# 源码
${CPP_DIR}/cmdutils.c
${CPP_DIR}/ffmpeg.c
${CPP_DIR}/ffmpeg_filter.c
${CPP_DIR}/ffmpeg_opt.c
${CPP_DIR}/ffmpeg_cmd.c
${CPP_DIR}/ffmpeg_hw.c
)
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 )
add_library(libavcodec
SHARED
IMPORTED )
set_target_properties(libavcodec
PROPERTIES IMPORTED_LOCATION
../../../../libs/armeabi-v7a/libavcodec.so )
add_library(libavfilter
SHARED
IMPORTED )
set_target_properties(libavfilter
PROPERTIES IMPORTED_LOCATION
../../../../libs/armeabi-v7a/libavfilter.so )
add_library(libavformat
SHARED
IMPORTED )
set_target_properties(libavformat
PROPERTIES IMPORTED_LOCATION
../../../../libs/armeabi-v7a/libavformat.so )
add_library(libavutil
SHARED
IMPORTED)
set_target_properties(libavutil
PROPERTIES IMPORTED_LOCATION
../../../../libs/armeabi-v7a/libavutil.so )
add_library(libswresample
SHARED
IMPORTED)
set_target_properties(libswresample
PROPERTIES IMPORTED_LOCATION
../../../../libs/armeabi-v7a/libswresample.so )
add_library(libswscale
SHARED
IMPORTED)
set_target_properties(libswscale
PROPERTIES IMPORTED_LOCATION
../../../../libs/armeabi-v7a/libswscale.so )
include_directories(libs/include)
target_link_libraries( myapplication
-Wl,--start-group
libavcodec libavfilter libavformat libavutil libswresample libswscale
-Wl,--end-group
${log-lib} )
2修改问题
精彩的部分来了,这时候运行项目,开始报错了。不过看很多文章都没出现这样的问题,我一直怀疑是不是我编译过程少了东西,懂的可以说一下。基本报错全是这样。fatal error: 'libavutil/thread.h' file not found
QQ截图20211206165256.png
注意!注意!注意!千万不要把报错的地方都删除掉。我就干过这么一次,好不容易泡成功了也没报错,结果生成的文件是0kb。
把缺少的文件全都复制过来。说一下我的方法。用Androidstudio打开ffmpeg项目。然后全局搜索文件,找错误提示说缺少的文件,注意包名然后复制到include里面。如果文件没有就创建文件夹。
QQ截图20211206165856.png
最后我的include文件夹下是这样
QQ截图20211206150412.png
再次提醒一下,不要复制错包名下的文件,很多文件在很多包下都有重名的。
3调试测试
package com.tj.myapplication;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import com.tj.myapplication.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
// Used to load the 'myapplication' library on application startup.
static {
System.loadLibrary("myapplication");
}
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.sampleText.setOnClickListener(view -> {
ceshi();
});
verifyStoragePermissions(this);
}
private static String[] PERMISSIONS_STORAGE = {
"android.permission.READ_EXTERNAL_STORAGE",
"android.permission.WRITE_EXTERNAL_STORAGE"};
//然后通过一个函数来申请
public static void verifyStoragePermissions(Activity activity) {
try {
//检测是否有写的权限
int permission = ActivityCompat.checkSelfPermission(activity,
"android.permission.WRITE_EXTERNAL_STORAGE");
if (permission != PackageManager.PERMISSION_GRANTED) {
// 没有写的权限,去申请写的权限,会弹出对话框
ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void ceshi() {
new Thread(new Runnable() {
@Override
public void run() {
//转换gif从1秒到4秒。
//ffmpeg -y -i /storage/emulated/0/DCIM/Camera/VID_20211205_103838.mp4 -ss 1.0 -t 4.0 /storage/emulated/0/DCIM/Camera/VID_20211205_103838.gif
//将视频的第0.1秒装换成png,类似获取视频封面。很多命令
String cmd = "ffmpeg -i /storage/emulated/0/DCIM/Camera/VID_20211205_103838.mp4 -t 0.1 /storage/emulated/0/DCIM/Camera/VID_20211205_1038381111.png";
cmdRun(cmd);
}
}).start();
}
private int cmdRun(String cmd) {
Log.e("命令", cmd);
String regulation = "[ \\t]+";
final String[] split = cmd.split(regulation);
return run(split);
}
public native int run(String[] cmd);
}
这里还有一个问题。主要是10以上的android版本。搞了半天,运行直接崩溃,以为有写错东西了,打了半天日志,才找到返回的错误码是13,权限问题。在AndroidManifest.xml 的<application中加上android:requestLegacyExternalStorage="true"就ok了。
参考地址: https://xch168.github.io/2018/10/26/android-invoke-ffmpeg-cmd/ Android NDK开发(四) 将FFmpeg移植到Android平台 - 简书
|