在Android中使用FFmpeg(android studio環境)

1.首先咱們須要一個已經編譯好的libffmpeg.so文件。(怎麼編譯是個大坑,能夠參考windows環境下編譯android中使用的FFmpeg,也能夠用網上下載的現成的,本文相關的github項目裏也有。),固然也要下載好ffmpeg的源碼,一會要用到。java

2.打開你的Android工程,在 src/main/ 目錄下新建 jni 目錄。並將libffmpeg.so文件丟進去。android

3.建立FFmpegKit.java。寫入以下代碼git

package codepig.ffmpegcldemo;

import android.os.AsyncTask;

public class FFmpegKit {
    public interface KitInterface{
        void onStart();
        void onProgress(int progress);
        void onEnd(int result);
    }

    static{
        System.loadLibrary("ffmpeg");
        System.loadLibrary("ffmpeginvoke");
    }

    public static int execute(String[] commands){
        return run(commands);
    }

    public static void execute(String[] commands, final KitInterface kitIntenrface){
        new AsyncTask<String[],Integer,Integer>(){
            @Override
            protected void onPreExecute() {
                if(kitIntenrface != null){
                    kitIntenrface.onStart();
                }
            }
            @Override
            protected Integer doInBackground(String[]... params) {
                return run(params[0]);
            }
            @Override
            protected void onProgressUpdate(Integer... values) {
                if(kitIntenrface != null){
                    kitIntenrface.onProgress(values[0]);
                }
            }
            @Override
            protected void onPostExecute(Integer integer) {
                if(kitIntenrface != null){
                    kitIntenrface.onEnd(integer);
                }
            }
        }.execute(commands);
    }

    public native static int run(String[] commands);
}

 

這個是用來調用ffmpeg可執行文件的。github

4.在終端中切到src/main/java文件夾下,輸入:windows

javah codepig.ffmpegcldemo.FFmpegKit

 

(這裏注意你本身的文件的實際位置)
而後就會在該目錄生成 codepig_ffmpegecldemo_FFmpegKit.h 文件,將這個文件移動到 jni 目錄。app

5.複製FFmpeg源碼文件 ffmpeg.h, ffmpeg.c, ffmpeg_opt.c, ffmpeg_filter.c,cmdutils.c, cmdutils.h, cmdutils_common_opts.h 到jni目錄下。
在 jni 目錄新建文件 Android.mk Application.mk codepig_ffmpegcldemo_FFmpegKit.c。ide

6.編輯ffmpeg.c,把函數

int main(int argc, char **argv)

 

更名爲gradle

int run(int argc, char **argv)

 

編輯ffmpeg.h, 在文件末尾添加函數申明:ui

int run(int argc, char **argv)

 

7.編輯cmdutils.c中的exit_program函數,刪掉函數中原來的內容, 添加 return ret;並修改函數的返回類型爲int。
長這樣:

int exit_program(int ret)
{
    return ret;
}

 

編輯cmdutils.h中exit_program的申明,把返回類型修改成int。
長這樣:

int exit_program(int ret);

 

8.在 codepig_ffmpegcldemo_FFmpegKit.c 中實現 codepig_ffmpegcldemo_FFmpegKit.h 中的方法。

#include <stdio.h>
#include "codepig_ffmpegcldemo_FFmpegKit.h"
#include "ffmpeg.h"
#include "logjam.h"

JNIEXPORT jint JNICALL Java_codepig_ffmpegcldemo_FFmpegKit_run
(JNIEnv *env, jclass obj, jobjectArray commands){
    //FFmpeg av_log() callback
    int argc = (*env)->GetArrayLength(env, commands);
    char *argv[argc];

    LOGD("Kit argc %d\n", argc);
    int i;
    for (i = 0; i < argc; i++) {
        jstring js = (jstring) (*env)->GetObjectArrayElement(env, commands, i);
        argv[i] = (char*) (*env)->GetStringUTFChars(env, js, 0);
        LOGD("Kit argv %s\n", argv[i]);
    }
    return run(argc, argv);
}

 

9.編輯Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := ffmpeg
LOCAL_SRC_FILES := libffmpeg.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := ffmpeginvoke
LOCAL_SRC_FILES := codepig_ffmpegcldemo_FFmpegKit.c ffmpeg.c ffmpeg_opt.c cmdutils.c ffmpeg_filter.c
LOCAL_C_INCLUDES := F:/demo/ffmpeg-3.0
LOCAL_LDLIBS := -llog -lz -ldl
LOCAL_SHARED_LIBRARIES := ffmpeg
include $(BUILD_SHARED_LIBRARY)

 

其中LOCAL_C_INCLUDES的值爲ffmpeg源碼文件夾地址

10.編輯Application.mk 文件

APP_ABI := armeabi-v7a
APP_PLATFORM := android-17

 

其中APP_ABI的值是支持的cpu類型。要支持多種cpu的話,能夠把類型寫一塊兒用空格隔開,好比

APP_ABI := armeabi-v7a x86

 

11.在終端中定位到jni目錄,執行ndk -build
成功後就會在libs文件夾生成相應的libffmpeg.so和libffmpeginvoke.so文件。這些so文件就是最終咱們用來調用的FFmpeg可執行文件。
若是出現以下錯誤提示

Android NDK: Could not find application project directory !
Android NDK: Please define the NDK_PROJECT_PATH variable to point to it.

 

則在Application.mk文件中添加

APP_BUILD_SCRIPT := Android.mk

 

若是出現以下提示

Android NDK: Your Android application project path contains spaces: 'F:/qzd android/Android/workspace/'
Android NDK: The Android NDK build cannot work here. Please move your project to a different location.

 

那大約是由於項目所在文件夾名稱有空格。更名就行了(感受好弱雞)

12.在build.gradle文件中修改下庫文件地址的指向

android {
    sourceSets {
        main {
            jniLibs.srcDirs = ['src/main/libs']
            jni.srcDirs=[]
        }
    }
}

 

如今終於能夠在android中使用ffmpeg庫了。

13.舉個栗子
(如下例子裏的videoUrl是原始視頻文件地址,imageUrl是水印圖片地址,musicUrl是音頻mp3地址,outputUrl是最終輸出視頻地址。)

(1) 給視頻添加圖片水印:

Runnable compoundRun=new Runnable() {
            @Override
            public void run() {
                String[] commands = new String[10];
                commands[0] = "ffmpeg";
                commands[1] = "-i";
                commands[2] = videoUrl;
                commands[3] = "-i";
                commands[4] = imageUrl;
                commands[5] = "-filter_complex";
                commands[6] = "overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2";
                commands[7] = "-codec:a";
                commands[8] = "copy";
                commands[9] = outputUrl;

                FFmpegKit.execute(commands, new FFmpegKit.KitInterface() {
                    @Override
                    public void onStart() {
                        Log.d("FFmpegLog LOGCAT","FFmpeg 命令行開始執行了...");
                    }

                    @Override
                    public void onProgress(int progress) {
                        Log.d("FFmpegLog LOGCAT","done com"+"FFmpeg 命令行執行進度..."+progress);
                    }

                    @Override
                    public void onEnd(int result) {
                        Log.d("FFmpegLog LOGCAT","FFmpeg 命令行執行完成...");
                    }
                });
            }
        };
        ThreadPoolUtils.execute(compoundRun);

 

(2) 合成音頻視頻

Runnable compoundRun=new Runnable() {
            @Override
            public void run() {
                String[] commands = new String[6];
                commands[0] = "ffmpeg";
                commands[1] = "-i";
                commands[2] = videoUrl;
                commands[3] = "-i";
                commands[4] = musicUrl;
                commands[5] = outputUrl;

                FFmpegKit.execute(commands, new FFmpegKit.KitInterface() {
                    @Override
                    public void onStart() {
                        Log.d("FFmpegLog LOGCAT","FFmpeg 命令行開始執行了...");
                    }

                    @Override
                    public void onProgress(int progress) {
                        Log.d("FFmpegLog LOGCAT","done com"+"FFmpeg 命令行執行進度..."+progress);
                    }

                    @Override
                    public void onEnd(int result) {
                        Log.d("FFmpegLog LOGCAT","FFmpeg 命令行執行完成...");
//                        getWindow().setFlags(0, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
//                        Message msg = new Message();
//                        msg.what = 1;
//                        mHandler.sendMessage(msg);
                    }
                });
            }
        };
        ThreadPoolUtils.execute(compoundRun);

 

(3)把這兩個命令寫一塊兒

Runnable compoundRun=new Runnable() {
            @Override
            public void run() {
                String[] commands = new String[11];
                commands[0] = "ffmpeg";
                commands[1] = "-i";
                commands[2] = videoUrl;
                commands[3] = "-i";
                commands[4] = imageUrl;
                commands[5] = "-filter_complex";
                commands[6] = "overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2";
                commands[7] = "-i";
                commands[8] = musicUrl;
                commands[9] = "-y";
                commands[10] = outputUrl;

                FFmpegKit.execute(commands, new FFmpegKit.KitInterface() {
                    @Override
                    public void onStart() {
                        Log.d("FFmpegLog LOGCAT","FFmpeg 命令行開始執行了...");
                    }

                    @Override
                    public void onProgress(int progress) {
                        Log.d("FFmpegLog LOGCAT","done com"+"FFmpeg 命令行執行進度..."+progress);
                    }

                    @Override
                    public void onEnd(int result) {
                        Log.d("FFmpegLog LOGCAT","FFmpeg 命令行執行完成...");
//                        getWindow().setFlags(0, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
                    }
                });
            }
        };
        ThreadPoolUtils.execute(compoundRun);

 

附:ffmpeg命令格式簡要說明
基本形式:

ffmpeg -i inputFile -參數名 參數值 …… outputFile;

頭尾不變,中間的參數順序無所謂。可是一個操做的參數必須寫一塊兒。
參數是以 -參數名+參數值 這樣的成對形式賦值。


參考:
ffmpeg參數解釋
FFmpeg經常使用命令大全,並簡單封裝
Android最簡單的基於FFmpeg的例子(四)---以命令行的形式來使用ffmpeg
本文github項目:
ffmpegCLDemo

做者連接:https://www.jianshu.com/p/e0c32c8b0ebc
相關文章
相關標籤/搜索