一、編譯
參考大神的帖子,親測一次編譯成功: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
1、創建新項目
(1)環境
Android Studio 3.2
NDK 17.1.4828580
SDK 28
(2)新建項目
看截圖吧,主要就是勾上這個東西,其他都默認就好。
2、複製文件
(1)複製so文件
都是按照默認目錄了,我在cpp目錄下新建了個文件夾ffmpeg/armeabi-v7a,將so包都複製進來了
(2)複製fftools的代碼
ffmpeg文件夾下新建文件夾include,然後將文件夾fftools裏面的部分文件複製到include文件夾中,並且在源代碼最外層將config.h複製進來
(3)複製源碼
本着寧殺錯不放過的原則,我將源碼根目錄下的所有文件夾(除了fftools)都複製到了include裏面。(參考大神的做法,發現好多文件都少了頭文件,然後一直報錯編譯不過)
3、創建jni的調用類
(1)Java類
創建FFmpegCmd類
(2)創建c類
看上圖,直接alt+enter就可以創建相關的方法了,Android Studio非常智能
然後將c類重命名爲ffmpeg_cmd_utils.c
(3)特別說明
之所以是c而不是cpp,我也很無奈,如果是cpp的話,編譯的時候爆了很多警告,並且是不給編譯,目前也沒有找到到底是什麼問題,畢竟我水平也是有限。
4、配置
(1)build.gradle
主要的幾個地方都標出來了
(2)CMakeLists.txt文件
主要就是將各個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} )
5、修改代碼
(1)cmdutils.c
註釋掉exit_program方法的內容:
(2)cmdutils.h
void show_help_children(const AVClass *class, int flags);
改成:
void show_help_children(const AVClass *clazz, int flags);
(3)ffmpeg.c
入口就是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);
}
}
(4)ffmpeg.h
在末尾加上ffmpeg_exec函數的函數聲明
(5)ffmpeg_cmd_utils.c
直接上調用代碼
#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、調用
(1)權限
讀寫手機內存的權限,這裏必須吐槽一下,我在手機是小米6,本着偷懶的原則,在設置的應用的詳情頁面點擊權限管理給app權限了之後,居然是不生效的,命令行一運行就崩潰,必須是代碼申請權限纔有用,我還以爲是編譯或者是代碼的問題,起碼在這一步浪費了三四個小時。不確定是MIUI的特有還是Android 8.0的鍋,儘量把工作做足,不要偷懶吧。
後來跑起來了的時候,大家應該都瞭解我當時的心情,有喜有淚,哭笑不得。
申請權限的代碼我就不貼上來了。
(2)調用
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();
(3)結果
(4)注意事項
命令行的所有文件的名字,不要帶特殊字符,如空格、換行符、emoji等。
總覺得我還漏了什麼沒說的,不管了,遇到問題了,記住,猥瑣發育,別浪,問題總會解決的。
廢話
說真的,我從ffmpeg 3.0的時候就對這件事情很感興趣了,但是一直卡在編譯so包這一步了,對linux系統不熟悉,一直不得要領,然後這一次是照着大神的腳步,一兩次就直接編譯出來so包了,我就知道這次是有戲了。
ffmpeg的命令行工具其實已經可以做不少比較好玩的事情了,預祝大家玩的開心。