參考大神的帖子,親測一次編譯成功:https://blog.csdn.net/bobcat_kay/article/details/80889398linux
鑑於之前查文檔的經驗,這裏附上編寫例子的時間:2018年7月22日android
我用的是ubantu,注意事項:c++
一、路徑這裏,home/ndk是不對的,真實路徑是home/電腦名/ndk,具體以cd ls命令的爲基準git
二、文件必需要在ubantu下載,我第一次是在win下下載,而後經過VMware傳過去的,結果出問題了,後來百度了一下發現了問題,就果斷直接ubantu直接下載了一個,而後就能夠了app
三、build.sh和confinger這兩個文件編輯的時候,必定要確保文本的格式不是doc而是unit(單詞應該是錯了的)格式ide
大神的帖子用的是mk的,而我這裏比較喜歡用cmake。函數
流程基本一致,但有些小細節,可能大神功力比較深,沒遇到啥問題,我是在這個步驟卡了大半個晚上。工具
附上項目地址:https://gitee.com/imxiaoyu_admin/CmdForFFmpeg4.0.2.gitgradle
Android Studio 3.2 ui
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 <android/log.h> #include <ffmpeg.h> #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的命令行工具其實已經能夠作很多比較好玩的事情了,預祝你們玩的開心。