前言
众所周知,ijkplayer是基于ffmpeg而实现的播放器,但是,ijkplayer只是实现了视频播放相关的功能,ffmpeg不仅可以用于视频的播放,也可以对视频进行其它操作,如格式转换、视频剪辑等,其中比较简单的方式是通过相关的命令进行操作,今天我们就在编译好的ijkplayer的so基础上实现ffmpeg命令行的功能。
准备
- 首先我们需要ijkplayer编译后的so,可以自己编译,如何编译ijkplayer在我之前的文章中已经介绍过了,可参考【Android音视频】Ijkplayer编译,也可用现有的。
- ffmpeg的一些头文件,比较多,文章结尾我会给出。
- JNI与NDK相关知识了解。
正文
注意:本文基于armv7a架构进行的
- 将libijkffmpeg.so文件放在libs下
- main目录下创建cpp文件夹,cpp文件夹下创建include文件夹,将ffmpeg的头文件放在include下,然后将ffmpeg源码中fftools中的ffmpeg.c,ffmpeg.h,cmdutils.c,cmdutils.h,ffmpeg_filter.c,ffmpeg_hw.c,ffmpeg_opt.c这几个文件放在cpp文件下
- 创建编写CMakeLists.txt
cmake_minimum_required(VERSION 3.10.2)
set(CURRENT_DIR ${CMAKE_SOURCE_DIR})
add_library(
native-lib
SHARED
src/main/cpp/native-lib.cpp src/main/cpp/cmdutils.c src/main/cpp/ffmpeg.c src/main/cpp/ffmpeg_filter.c src/main/cpp/ffmpeg_hw.c src/main/cpp/ffmpeg_opt.c)
include_directories(src/main/cpp/include/)
add_library( ijkffmpeg
SHARED
IMPORTED )
set_target_properties(
ijkffmpeg
PROPERTIES IMPORTED_LOCATION
${CURRENT_DIR}/libs/armeabi-v7a/libijkffmpeg.so)
target_link_libraries(
native-lib
ijkffmpeg
)
- 修改ffmpeg.c,ffmpeg.h,cmdutils.c,cmdutils.h文件
- 对ffmpeg.c中的main函数进行修改,在函数底部修改为以下内容,函数内调用exit_program()的地方换成return 1; 并将main函数名改为ffmpeg_cmd
if ((decode_error_stat[0] + decode_error_stat[1]) * max_error_rate < decode_error_stat[1])
exit_program(69);
//exit_program(received_nb_signals ? 255 : main_return_code);
nb_filtergraphs = 0;
nb_output_files = 0;
nb_output_streams = 0;
nb_input_files = 0;
nb_input_streams = 0;
return main_return_code;
- 修改ffmpg.h,在ffmpeg.h中添加函数声明 int ffmpeg_cmd(int argc, char **argv);
- 修改cmdutils.c,将函数exit_program改为以下内容
void exit_program(int ret)
{
// if (program_exit)
// program_exit(ret);
// exit(ret);
}
修改cmdutils.h,将函数void exit_program(int ret) av_noreturn;改为void exit_program(int ret) ;去除av_noreturn,不然对已经存在的文件的覆盖修改时会报错。
编写JNI方法,C++
#include <jni.h>
extern "C" {
#include "ffmpeg.h"
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_yo_ndk_FFmpegCmd_run(JNIEnv *env, jclass clazz, jobjectArray cmd) {
int len = (*env).GetArrayLength(cmd);
char *argv[len];
int i;
for(i = 0;i < len;++i){
argv[i] = (char *) (*env).GetStringUTFChars((jstring) (*env).GetObjectArrayElement(cmd,i),0);
}
return ffmpeg_cmd(len,argv);
}
- 编写Java方法,
public class FFmpegCmd {
static {
System.loadLibrary("ijkffmpeg");
System.loadLibrary("native-lib");
}
public native static int run(String[] cmd);
}
- build.gradle配置
android {
...
defaultConfig {
....
...
externalNativeBuild {
cmake {
cppFlags ""
abiFilters "armeabi-v7a"
}
}
}
....
externalNativeBuild {
cmake {
path file('CMakeLists.txt')
version "3.10.2"
}
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
packagingOptions{
pickFirst "lib/armeabi-v7a/libijkffmpeg.so"
}
}
至此结束。
结语
本文介绍的方法是比较简单粗略,里面会存在不足之处,比如那些调用exit_program()方法的地方都没做处理,可能会带来隐患,具体就要进一步处理了,ffmpeg太多知识点了,这算是一次了解ffmpeg的体验。