Android Studio+NDK+Cmake 移植FFmpeg-4.0.2命令行工具

一、編譯

參考大神的帖子,親測一次編譯成功: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的命令行工具其實已經可以做不少比較好玩的事情了,預祝大家玩的開心。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章