咱们编译FFmpeg通常在Linux的系统上进行编译,固然windows也是能够的,这里讲解一下在Linux系统上编译。html
ffmpeg
进行解压ffmpeg
文件目录,修改configure
文件SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)' LIB_INSTALL_EXTRA_CMD='?(RANLIB) "$(LIBDIR)/$(LIBNAME)"' SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)' SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR) $(SLIBNAME)' 复制代码
替换成java
SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)' LIB_INSTALL_EXTRA_CMD='?(RANLIB)"$(LIBDIR)/$(LIBNAME)"' SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)' SLIB_INSTALL_LINKS='$(SLIBNAME)' 复制代码
#!/bin/bash # 清空上次的编译 make clean #你本身的NDK路径. export NDK=/home/anjoiner/Documents/AnJoiner/ffmpeg/ndk function build_android { echo "Compiling FFmpeg for $CPU" ./configure \ --prefix=$PREFIX \ --enable-neon \ --enable-hwaccels \ --enable-gpl \ --enable-postproc \ --enable-shared \ --enable-jni \ --enable-mediacodec \ --enable-decoder=h264_mediacodec \ --disable-static \ --disable-doc \ --enable-ffmpeg \ --disable-ffplay \ --disable-ffprobe \ --enable-avdevice \ --disable-doc \ --disable-symver \ --cross-prefix=$CROSS_PREFIX \ --target-os=android \ --arch=$ARCH \ --cpu=$CPU \ --enable-cross-compile \ --sysroot=$SYSROOT \ --extra-cflags="-Os -fpic $OPTIMIZE_CFLAGS" \ --extra-ldflags="$ADDI_LDFLAGS" \ $ADDITIONAL_CONFIGURE_FLAG make clean make make install echo "The Compilation of FFmpeg for $CPU is completed" } #armv8-a ARCH=arm64 CPU=armv8-a TOOLCHAIN=$NDK/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64 SYSROOT=$NDK/platforms/android-21/arch-$ARCH/ CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android- PREFIX=$(pwd)/android/$CPU OPTIMIZE_CFLAGS="-march=$CPU" build_android #armv7-a ARCH=arm CPU=armv7-a TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64 SYSROOT=$NDK/platforms/android-21/arch-$ARCH/ CROSS_PREFIX=$TOOLCHAIN/bin/arm-linux-androideabi- PREFIX=$(pwd)/android/$CPU OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU " build_android #x86 ARCH=x86 CPU=x86 TOOLCHAIN=$NDK/toolchains/x86-4.9/prebuilt/linux-x86_64 SYSROOT=$NDK/platforms/android-21/arch-$ARCH/ CROSS_PREFIX=$TOOLCHAIN/bin/i686-linux-android- PREFIX=$(pwd)/android/$CPU OPTIMIZE_CFLAGS="-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32" build_android #x86_64 ARCH=x86_64 CPU=x86-64 TOOLCHAIN=$NDK/toolchains/x86_64-4.9/prebuilt/linux-x86_64 SYSROOT=$NDK/platforms/android-21/arch-$ARCH/ CROSS_PREFIX=$TOOLCHAIN/bin/x86_64-linux-android- PREFIX=$(pwd)/android/$CPU OPTIMIZE_CFLAGS="-march=$CPU -msse4.2 -mpopcnt -m64 -mtune=intel" build_android 复制代码
注意:当执行./build_android.sh
的时候,出现权限不足,那么必定要给这个文件添加可执行权限linux
chmod 777 build_android.sh
复制代码
这样就能够进行编译生成Android下的ffmpeg
android
若是在编译的时候出现以下问题:c++
ffbuild/common.mak:60: recipe for target 'libavformat/udp.o' failed make: *** [libavformat/udp.o] Error 1 复制代码
将libavformat/udp.c
第290~295行进行注释windows
// mreqs.imr_multiaddr.s_addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr; // if (local_addr) // mreqs.imr_interface= ((struct sockaddr_in *)local_addr)->sin_addr; // else // mreqs.imr_interface.s_addr= INADDR_ANY; // mreqs.imr_sourceaddr.s_addr = ((struct sockaddr_in *)&sources[i])->sin_addr.s_addr; 复制代码
这样以后,基本就是ok的,在咱们ffmpeg
的文件夹下,会生成一个ffmpeg/android/armv7-a
的文件目录,里面内容以下:bash
jni
的文件夹。ffmpeg/android/armv7-a/include
下的全部文件拷贝进入jni
文件夹。jni
下新建一个prebuilt
的文件夹,将ffmpeg/android/armv7-a/lib
下的so文件所有拷贝到prebuilt
之下。ffmpeg/fftools
文件下的以下文件拷贝进入jni
ffmpeg.c
文件,找到int main(int argc, char **argv)
函数,将其替换为int run(int argc, char **argv)
run(int argc, char **argv)
末尾(retrun 以前)加上如上以下代码: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;
复制代码
ffmpeg.h
文件末尾加上int run(int argc, char **argv);
复制代码
ffmpeg.c
文件,注释掉run(int argc, char **argv)
下的全部exit_program
函数,大体代码以下:int run(int argc, char **argv) { int i, ret; BenchmarkTimeStamps ti; init_dynload(); register_exit(ffmpeg_cleanup); setvbuf(stderr,NULL,_IONBF,0); /* win32 runtime needs this */ av_log_set_flags(AV_LOG_SKIP_REPEATED); parse_loglevel(argc, argv, options); if(argc>1 && !strcmp(argv[1], "-d")){ run_as_daemon=1; av_log_set_callback(log_callback_null); argc--; argv++; } #if CONFIG_AVDEVICE avdevice_register_all(); #endif avformat_network_init(); show_banner(argc, argv, options); /* parse options and open all input/output files */ ret = ffmpeg_parse_options(argc, argv); if (ret < 0); // exit_program(1); if (nb_output_files <= 0 && nb_input_files == 0) { show_usage(); av_log(NULL, AV_LOG_WARNING, "Use -h to get full help or, even better, run 'man %s'\n", program_name); // exit_program(1); } /* file converter / grab */ if (nb_output_files <= 0) { av_log(NULL, AV_LOG_FATAL, "At least one output file must be specified\n"); // exit_program(1); } // if (nb_input_files == 0) { // av_log(NULL, AV_LOG_FATAL, "At least one input file must be specified\n"); // exit_program(1); // } for (i = 0; i < nb_output_files; i++) { if (strcmp(output_files[i]->ctx->oformat->name, "rtp")) want_sdp = 0; } current_time = ti = get_benchmark_time_stamps(); if (transcode() < 0); // exit_program(1); if (do_benchmark) { int64_t utime, stime, rtime; current_time = get_benchmark_time_stamps(); utime = current_time.user_usec - ti.user_usec; stime = current_time.sys_usec - ti.sys_usec; rtime = current_time.real_usec - ti.real_usec; av_log(NULL, AV_LOG_INFO, "bench: utime=%0.3fs stime=%0.3fs rtime=%0.3fs\n", utime / 1000000.0, stime / 1000000.0, rtime / 1000000.0); } av_log(NULL, AV_LOG_DEBUG, "%"PRIu64" frames successfully decoded, %"PRIu64" decoding errors\n", decode_error_stat[0], decode_error_stat[1]); 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; 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; } 复制代码
在jni
文件下新建一个ffmpeg-invoke.cpp
的c++文件,将以下代码写入markdown
#include <jni.h> #include <string.h> #include "android/log.h" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "ffmpeg-invoke", __VA_ARGS__) extern "C"{ #include "ffmpeg.h" #include "libavcodec/jni.h" } extern "C" JNIEXPORT jint JNICALL Java_com_coder_ffmpeg_jni_FFmpegCmd_run(JNIEnv *env, jclass type, jint cmdLen, jobjectArray cmd) { //set java vm JavaVM *jvm = NULL; env->GetJavaVM(&jvm); av_jni_set_java_vm(jvm, NULL); char *argCmd[cmdLen] ; jstring buf[cmdLen]; for (int i = 0; i < cmdLen; ++i) { buf[i] = static_cast<jstring>(env->GetObjectArrayElement(cmd, i)); char *string = const_cast<char *>(env->GetStringUTFChars(buf[i], JNI_FALSE)); argCmd[i] = string; LOGD("argCmd=%s",argCmd[i]); } int retCode = run(cmdLen, argCmd); LOGD("ffmpeg-invoke: retCode=%d",retCode); return retCode; } 复制代码
需注意的是 Java_com_coder_ffmpeg_jni_FFmpegCmd_run
中 com_coder_ffmpeg_jni
表示FFmpegCmd
所在你Android项目的位置。app
在jni
文件夹下,新建Android.mk
的文件,将以下代码拷贝,可是须要注意的是将 LOCAL_C_INCLUDES
路径替换成你源码所在位置。jvm
// 将此处的路径改成你ffmepg源码所在位置
LOCAL_C_INCLUDES := /home/anjoiner/Documents/AnJoiner/ffmpeg
复制代码
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libavdevice
LOCAL_SRC_FILES := prebuilt/libavdevice.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libavutil
LOCAL_SRC_FILES := prebuilt/libavutil.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libswresample
LOCAL_SRC_FILES := prebuilt/libswresample.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libswscale
LOCAL_SRC_FILES := prebuilt/libswscale.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libavcodec
LOCAL_SRC_FILES := prebuilt/libavcodec.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libavformat
LOCAL_SRC_FILES := prebuilt/libavformat.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libavfilter
LOCAL_SRC_FILES := prebuilt/libavfilter.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libpostproc
LOCAL_SRC_FILES := prebuilt/libpostproc.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := ffmpeg-invoke
LOCAL_SRC_FILES :=ffmpeg-invoke.cpp \
cmdutils.c \
ffmpeg_filter.c \
ffmpeg_opt.c \
ffmpeg_hw.c \
ffmpeg.c
// 将此处的路径改成你ffmepg源码所在位置
LOCAL_C_INCLUDES := /home/anjoiner/Documents/AnJoiner/ffmpeg
LOCAL_LDLIBS := -llog -ljnigraphics -lz -landroid -lm -pthread -L$(SYSROOT)/usr/lib
LOCAL_SHARED_LIBRARIES := libavdevice libavcodec libavfilter libavformat libavutil libswresample libswscale libpostproc
include $(BUILD_SHARED_LIBRARY)
复制代码
在jni
文件夹下,新建Application.mk并将以下代码拷贝进入
APP_ABI := armeabi-v7a
APP_PLATFORM := android-21
APP_OPTIM := release
APP_STL := stlport_static
复制代码
到此基本上的配置已经完成,而后在jni
文件下运行ndk-build
/home/anjoiner/Documents/AnJoiner/build/jni# /home/anjoiner/Documents/AnJoiner/ffmpeg/ndk/ndk-build 复制代码
当出现以下代码就表示成功了
[armeabi-v7a] SharedLibrary : libffmpeg-invoke.so
[armeabi-v7a] Install : libffmpeg-invoke.so => libs/armeabi-v7a/libffmpeg-invoke.so
[armeabi-v7a] Install : libpostproc.so => libs/armeabi-v7a/libpostproc.so
[armeabi-v7a] Install : libswresample.so => libs/armeabi-v7a/libswresample.so
[armeabi-v7a] Install : libswscale.so => libs/armeabi-v7a/libswscale.so
复制代码
ava_com_coder_ffmpeg_jni_FFmpegCmd_run
中的com.coder.ffmpeg
。jni
的同级目录下,会生成一个libs
的目录,将这个目录下的armeabi-v7a
文件拷贝到你所在的Android项目中的app/src/main/jniLibs
下FFmpegCmd
/** * @author: AnJoiner * @datetime: 19-7-30 */ public class FFmpegCmd { static { System.loadLibrary("avdevice"); System.loadLibrary("avutil"); System.loadLibrary("avcodec"); System.loadLibrary("swresample"); System.loadLibrary("avformat"); System.loadLibrary("swscale"); System.loadLibrary("avfilter"); System.loadLibrary("postproc"); System.loadLibrary("ffmpeg-invoke"); } private static native int run(int cmdLen, String[] cmd); public static int runCmd(String[] cmd){ return run(cmd.length,cmd); } } 复制代码
拷贝进来run
方法名会出现红色,不用管他,
MainActivity
中进行调用,此方法是将音频进行剪切public class MainActivity extends AppCompatActivity { // Used to load the 'native-lib' library on application startup. static { System.loadLibrary("native-lib"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100); } // Example of a call to a native method TextView tv = findViewById(R.id.sample_text); tv.setText(stringFromJNI()); findViewById(R.id.btn_test).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { ffmpegTest(); } }); } /** * A native method that is implemented by the 'native-lib' native library, * which is packaged with this application. */ public native String stringFromJNI(); private void ffmpegTest() { new Thread() { @Override public void run() { long startTime = System.currentTimeMillis(); String input = Environment.getExternalStorageDirectory().getPath() + File.separator + "DCIM" + File.separator + "test.mp3"; String output = Environment.getExternalStorageDirectory().getPath() + File.separator + "DCIM" + File.separator + "output.mp3"; String cmd = "ffmpeg -y -i %s -vn -acodec copy -ss %s -t %s %s"; String result = String.format(cmd, input, "00:00:30", "00:00:40", output); FFmpegCmd.runCmd(result.split(" ")); Log.d("FFmpegTest", "run: 耗时:" + (System.currentTimeMillis() - startTime)); } }.start(); } 复制代码
若是上述步骤不出错,基本没什么问题,在资料的参考下,我也弄了好几天,但幸亏结果仍是好的~
前面使用过老版本NDK-r14b
编译FFmpeg
,其实质是经过gcc去进行编译,可是最新的NDK版本中已经不使用gcc去编译,而是使用clang去进行编译.这里贴上最新配置文件代码
#!/bin/bash # 清空上次的编译 make clean #你本身的NDK路径. export NDK=/home/anjoiner/Android/Sdk/ndk-bundle TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64/ API=29 function build_android { echo "Compiling FFmpeg for $CPU" ./configure \ --prefix=$PREFIX \ --enable-neon \ --enable-hwaccels \ --enable-gpl \ --enable-postproc \ --enable-shared \ --enable-jni \ --enable-mediacodec \ --enable-decoder=h264_mediacodec \ --disable-static \ --disable-doc \ --enable-ffmpeg \ --disable-ffplay \ --disable-ffprobe \ --enable-avdevice \ --disable-doc \ --disable-symver \ --cross-prefix=$CROSS_PREFIX \ --target-os=android \ --arch=$ARCH \ --cpu=$CPU \ --cc=$CC --cxx=$CXX --enable-cross-compile \ --sysroot=$SYSROOT \ --extra-cflags="-Os -fpic $OPTIMIZE_CFLAGS" \ --extra-ldflags="$ADDI_LDFLAGS" \ $ADDITIONAL_CONFIGURE_FLAG make clean make make install echo "The Compilation of FFmpeg for $CPU is completed" } #armv8-a ARCH=arm64 CPU=armv8-a CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++ SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android- PREFIX=$(pwd)/android/$CPU OPTIMIZE_CFLAGS="-march=$CPU" build_android #armv7-a ARCH=arm CPU=armv7-a CC=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang++ SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot CROSS_PREFIX=$TOOLCHAIN/bin/arm-linux-androideabi- PREFIX=$(pwd)/android/$CPU OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU " build_android #x86 ARCH=x86 CPU=x86 CC=$TOOLCHAIN/bin/i686-linux-android$API-clang CXX=$TOOLCHAIN/bin/i686-linux-android$API-clang++ SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot CROSS_PREFIX=$TOOLCHAIN/bin/i686-linux-android- PREFIX=$(pwd)/android/$CPU OPTIMIZE_CFLAGS="-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32" build_android #x86_64 ARCH=x86_64 CPU=x86-64 CC=$TOOLCHAIN/bin/x86_64-linux-android$API-clang CXX=$TOOLCHAIN/bin/x86_64-linux-android$API-clang++ SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot CROSS_PREFIX=$TOOLCHAIN/bin/x86_64-linux-android- PREFIX=$(pwd)/android/$CPU OPTIMIZE_CFLAGS="-march=$CPU -msse4.2 -mpopcnt -m64 -mtune=intel" build_android 复制代码