1.首先我們需要一個已經(jīng)編譯好的libffmpeg.so文件。(怎么編譯是個大坑液南,可以參考windows環(huán)境下編譯android中使用的FFmpeg壳猜,也可以用網(wǎng)上下載的現(xiàn)成的,本文相關(guān)的github項目里也有滑凉。)统扳,當(dāng)然也要下載好ffmpeg的源碼喘帚,一會要用到。
2.打開你的Android工程,在 src/main/ 目錄下新建 jni 目錄咒钟。并將libffmpeg.so文件丟進去吹由。
3.創(chuàng)建FFmpegKit.java。寫入如下代碼
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);
}
這個是用來調(diào)用ffmpeg可執(zhí)行文件的朱嘴。
4.在終端中切到src/main/java文件夾下倾鲫,輸入:
javah codepig.ffmpegcldemo.FFmpegKit
(這里注意你自己的文件的實際位置)
然后就會在該目錄生成 codepig_ffmpegecldemo_FFmpegKit.h 文件,將這個文件移動到 jni 目錄。
5.復(fù)制FFmpeg源碼文件 ffmpeg.h, ffmpeg.c, ffmpeg_opt.c, ffmpeg_filter.c萍嬉,cmdutils.c, cmdutils.h, cmdutils_common_opts.h 到j(luò)ni目錄下蛀序。
在 jni 目錄新建文件 Android.mk Application.mk codepig_ffmpegcldemo_FFmpegKit.c社搅。
6.編輯ffmpeg.c,把
int main(int argc, char **argv)
改名為
int run(int argc, char **argv)
編輯ffmpeg.h, 在文件末尾添加函數(shù)申明:
int run(int argc, char **argv)
7.編輯cmdutils.c中的exit_program函數(shù),刪掉函數(shù)中原來的內(nèi)容, 添加 return ret次企;并修改函數(shù)的返回類型為int昧廷。
長這樣:
int exit_program(int ret)
{
return ret;
}
編輯cmdutils.h中exit_program的申明被碗,把返回類型修改為int奏属。
長這樣:
int exit_program(int ret);
8.在 codepig_ffmpegcldemo_FFmpegKit.c 中實現(xiàn) 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.在終端中定位到j(luò)ni目錄贿堰,執(zhí)行ndk -build
成功后就會在libs文件夾生成相應(yīng)的libffmpeg.so和libffmpeginvoke.so文件辙芍。這些so文件就是最終我們用來調(diào)用的FFmpeg可執(zhí)行文件。
如果出現(xiàn)如下錯誤提示
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
如果出現(xiàn)如下提示
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=[]
}
}
}
現(xiàn)在終于可以在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 命令行開始執(zhí)行了...");
}
@Override
public void onProgress(int progress) {
Log.d("FFmpegLog LOGCAT","done com"+"FFmpeg 命令行執(zhí)行進度..."+progress);
}
@Override
public void onEnd(int result) {
Log.d("FFmpegLog LOGCAT","FFmpeg 命令行執(zhí)行完成...");
}
});
}
};
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 命令行開始執(zhí)行了...");
}
@Override
public void onProgress(int progress) {
Log.d("FFmpegLog LOGCAT","done com"+"FFmpeg 命令行執(zhí)行進度..."+progress);
}
@Override
public void onEnd(int result) {
Log.d("FFmpegLog LOGCAT","FFmpeg 命令行執(zhí)行完成...");
// 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 命令行開始執(zhí)行了...");
}
@Override
public void onProgress(int progress) {
Log.d("FFmpegLog LOGCAT","done com"+"FFmpeg 命令行執(zhí)行進度..."+progress);
}
@Override
public void onEnd(int result) {
Log.d("FFmpegLog LOGCAT","FFmpeg 命令行執(zhí)行完成...");
// getWindow().setFlags(0, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
});
}
};
ThreadPoolUtils.execute(compoundRun);
附:ffmpeg命令格式簡要說明
基本形式:
ffmpeg -i inputFile -參數(shù)名 參數(shù)值 …… outputFile;
頭尾不變徘层,中間的參數(shù)順序無所謂。但是一個操作的參數(shù)必須寫一起利职。
參數(shù)是以 -參數(shù)名+參數(shù)值 這樣的成對形式賦值趣效。
參考:
ffmpeg參數(shù)解釋
FFmpeg常用命令大全,并簡單封裝
Android最簡單的基于FFmpeg的例子(四)---以命令行的形式來使用ffmpeg
本文github項目:
ffmpegCLDemo
(誠邀各位多去點幾個star)