不知道以前誰說過,圖片太單調(diào)文字太枯燥,小視頻剛剛好。現(xiàn)在各種自拍APP,直播APP都火的一塌糊涂,一年時間過大半,我都不記得上次寫文章是什么時候了,廢話不多說,今天這里就是分享給一下我自己學(xué)習(xí)用FFmepg來做小視頻的過程,內(nèi)容估計有點多,準(zhǔn)備分兩篇來完成,炎炎夏日就別嗑瓜子了,帶上小板凳和西瓜跟著來。
目標(biāo)功能:
1.編譯出ffmpeg的so文件
2.可通過傳遞命令的方式調(diào)用ffmpeg功能。
1.新建一個項目FFmpegVideo
截一張圖表示一下
2.下載ndk以及ffmepg
我這里下載的ndk版本是android-ndk_r14b
ffmepg版本是ffmpeg-3.3.2
也許當(dāng)你看到這邊篇文的時候最新版本已經(jīng)比這個更高了,如果后面編譯出現(xiàn)問題,很有可能跟版本有關(guān)系,所以如果你編譯其他版本出現(xiàn)問題,需要你自己看懂腳本靈活變動。
ndk的環(huán)境變量的配置我這里就不多說了,不同的系統(tǒng)配置不一樣,自行百度。
3.NDK配置好了嗎?配置好了開始準(zhǔn)備編譯ffmpeg相關(guān)腳本文件。
打開ffmpeg-3.3.2文件夾并且創(chuàng)建一個文件夾(ffmpegtemp)和一個文件(build_android_armeabi_armeabi-v7a.sh)
(2)這里最主要的就是build_android_armeabi_armeabi-v7a.sh這個文件的內(nèi)容,非常非常非常重要,把以下內(nèi)容復(fù)制到你的這個文件中
#!/bin/bash
export TMPDIR=/Users/tangyx/Documents/ffmpeg-3.3.2/ffmpegtemp #這句很重要,不然會報錯 unable to create temporary file in
# NDK的路徑,根據(jù)自己的安裝位置進行設(shè)置
NDK=/Users/tangyx/Documents/android-ndk_r14b
# 編譯針對的平臺,可以根據(jù)自己的需求進行設(shè)置
# 這里選擇最低支持android-14, arm架構(gòu),生成的so庫是放在
# libs/armeabi文件夾下的,若針對x86架構(gòu),要選擇arch-x86
PLATFORM=$NDK/platforms/android-14/arch-arm
# 工具鏈的路徑,根據(jù)編譯的平臺不同而不同
# arm-linux-androideabi-4.9與上面設(shè)置的PLATFORM對應(yīng),4.9為工具的版本號,
# 根據(jù)自己安裝的NDK版本來確定,一般使用最新的版本
TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64
function build_one
{
./configure \
--prefix=$PREFIX \
--target-os=linux \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--arch=arm \
--sysroot=$PLATFORM \
--extra-cflags="-I$PLATFORM/usr/include" \
--cc=$TOOLCHAIN/bin/arm-linux-androideabi-gcc \
--nm=$TOOLCHAIN/bin/arm-linux-androideabi-nm \
--disable-shared \
--enable-runtime-cpudetect \
--enable-gpl \
--enable-small \
--enable-cross-compile \
--disable-debug \
--enable-static \
--disable-doc \
--disable-asm \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-postproc \
--disable-avdevice \
--disable-symver \
--disable-stripping \
$ADDITIONAL_CONFIGURE_FLAG
sed -i '' 's/HAVE_LRINT 0/HAVE_LRINT 1/g' config.h
sed -i '' 's/HAVE_LRINTF 0/HAVE_LRINTF 1/g' config.h
sed -i '' 's/HAVE_ROUND 0/HAVE_ROUND 1/g' config.h
sed -i '' 's/HAVE_ROUNDF 0/HAVE_ROUNDF 1/g' config.h
sed -i '' 's/HAVE_TRUNC 0/HAVE_TRUNC 1/g' config.h
sed -i '' 's/HAVE_TRUNCF 0/HAVE_TRUNCF 1/g' config.h
sed -i '' 's/HAVE_CBRT 0/HAVE_CBRT 1/g' config.h
sed -i '' 's/HAVE_RINT 0/HAVE_RINT 1/g' config.h
make clean
make -j4
make install
$TOOLCHAIN/bin/arm-linux-androideabi-ld \
-rpath-link=$PLATFORM/usr/lib \
-L$PLATFORM/usr/lib \
-L$PREFIX/lib \
-soname libffmpeg.so -shared -nostdlib -Bsymbolic --whole-archive --no-undefined -o \
$PREFIX/libffmpeg.so \
libavcodec/libavcodec.a \
libavfilter/libavfilter.a \
libswresample/libswresample.a \
libavformat/libavformat.a \
libavutil/libavutil.a \
libswscale/libswscale.a \
-lc -lm -lz -ldl -llog --dynamic-linker=/system/bin/linker \
$TOOLCHAIN/lib/gcc/arm-linux-androideabi/4.9.x/libgcc.a
}
# arm v7vfp
CPU=armv7-a
OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU "
PREFIX=./android/$CPU-vfp
ADDITIONAL_CONFIGURE_FLAG=
build_one
這里提醒吃瓜的朋友還是耐心看一下這個腳本的內(nèi)容,因為我這次文章只會編譯arm的so,其他框架(x86,arm64等)下的腳本我暫時不會提供,所以大概解釋一下這個腳本的重要地方
圖中紅色地方就是每次需要編譯不同框架下修改的地方。
這些對應(yīng)的文件路徑主要是你的ndk文件路徑,其他不需要你去改動。
下半部分:
特別是第一個紅色框中的lib這里默認是lib,我在編譯x86_64框架的so時候忽略了這里,然后無論怎么樣也編譯不過,真的差點哭到放棄。
這里的lib的同級文件夾還有l(wèi)ib64,所以當(dāng)你編譯64位的框架(x86_64)的時候一定要修改引用到lib64的文件夾,否則會出現(xiàn)報錯,出現(xiàn)一大堆找不到內(nèi)容的錯誤。
4.好了腳本準(zhǔn)備就緒了,我這里的腳本是編譯出支持arm以及arm-v7a的so。
打開你的終端cd切換到你的ffmpeg-3.3.2下
切換完成后,就直接執(zhí)行寫好的腳本。
先這樣
chmod +x build_android_armeabi_armeabi-v7a.sh
然后這樣
./build_android_armeabi_armeabi-v7a.sh
最后這樣感覺卡住了一樣(耐心等待)
差不多你接杯水的時間回來,然后就這樣了
這里編譯的速度根據(jù)各自的電腦性能,我這里把水接好,在泡上咖啡時間剛剛差不多。
5等待編譯完成后,打開ffmpeg-3.3.2文件夾發(fā)現(xiàn)多了一個文件夾(android)和文件(config.h)
文件就不管了,但是也不能刪除,打開android文件夾不出意外是這樣的
看見紅色部分沒有?看見沒有?這就是最重要的成果,下面就需要把他放在我們對應(yīng)的項目中去編譯出可用的so文件,這個文件只是用來動態(tài)生成框架的so包,還不能直接在android中調(diào)用。
6回到Android Studio的新家項目FFmpegVideo中,在main下面新建文件夾jni
在這里需要說明一下,jni下編譯so文件對應(yīng)我們java類的native方法提供調(diào)用,一般情況都是需要編寫c++和java對應(yīng)的方法來進行調(diào)用,在這里如果按照方法的調(diào)用的模式,對于新手或者剛接觸的人來說簡直就是噩夢,所以這里有大神提供修改了ffmepg的c++源文件,直接可以通過命令的方式來調(diào)用ffmpeg的功能,真是前人栽樹,后人乘涼,極大省去了很多麻煩,這樣只需要了解學(xué)習(xí)ffmpeg的命令你就可以完成很多事情。
關(guān)于修改源碼的內(nèi)容和地方請通過這里查看(如果你感興趣可以了解一下,反正我對c是個半吊子水平。)
接下來繼續(xù)我們的搭建開發(fā)流程
- 新建一個包名和一個用來調(diào)用ffmpeg的類
2.獲取c語言接口的函數(shù)聲明
點擊Android Studio最下方的Terminal窗口
切換到項目下的java目錄下
然后執(zhí)行以下命令
javah com.ihubin.ffmpegstudy.FFmpegKit
執(zhí)行完成以后,刷新一下,src/main/java下會生成一個文件(根據(jù)項目不同的包名生成的文件名字不一樣)com_tangyx_video_ffmpeg_FFmpegRun.h</b>
把這個文件移動到j(luò)ni文件下
還記得上面ffmpeg-3.3.2下android文件夾armv7-a-vfp中生成的那個libffmpeg.so文件么,拷貝它到項目的jni目錄下
復(fù)制FFmpeg源碼文件 ffmpeg.h, ffmpeg.c, ffmpeg_opt.c, ffmpeg_filter.c,cmdutils.c, cmdutils.h, cmdutils_common_opts.h 到j(luò)ni目錄下。
我這里的復(fù)制的ffmpeg.c和ffmpeg.h兩個文件已經(jīng)根據(jù)上面大神指導(dǎo)的方式修改好了,可以直接使用,最后的源碼我也會上傳,各位吃瓜群眾可以直接使用。
在jni目錄下手動創(chuàng)建三個文件
Android.mk
Application.mk
com_tangyx_video_ffmpeg_FFmpegRun.c(根據(jù)項目不同的包名生成的文件名字不一樣,對應(yīng)上面的.h文件)
**Android.mk **的內(nèi)容:
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 := com_tangyx_video_ffmpeg_FFmpegRun.c ffmpeg.c ffmpeg_opt.c cmdutils.c ffmpeg_filter.c
LOCAL_C_INCLUDES := /Users/tangyx/Documents/ffmpeg-3.3.2
LOCAL_LDLIBS := -llog -lz -ldl
LOCAL_SHARED_LIBRARIES := ffmpeg
include $(BUILD_SHARED_LIBRARY)
LOCAL_C_INCLUDES的路徑記得修改為你當(dāng)前ffmepg文件夾的路徑。
LOCAL_SRC_FILES第一個c文件的引用記得改為你當(dāng)前jni下生成的c文件。
** Application.mk **的內(nèi)容:
APP_ABI := armeabi armeabi-v7a
APP_BUILD_SCRIPT := Android.mk
APP_PLATFORM := android-15
這里能夠支持生成arm可用的so文件(不支持arm64)
com_tangyx_video_ffmpeg_FFmpegRun.c的內(nèi)容:
#include "com_tangyx_video_ffmpeg_FFmpegRun.h"
#include "ffmpeg.h"
#include <string.h>
/*
* Class: com_example_ffmpeg_FFmpegKit
* Method: run
* Signature: (Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_tangyx_video_ffmpeg_FFmpegRun_run(JNIEnv *env,
jclass obj, jobjectArray commands) {
int argc = (*env)->GetArrayLength(env, commands);
char *argv[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);
}
return run(argc, argv);
}
第一行的include引用你當(dāng)前jni下生成的.h文件
Java_com_tangyx_video_ffmpeg_FFmpegRun_run需要改成你項目對應(yīng)的包名和方法名(聰明的你仔細看一下就知道這個規(guī)則)
上面就是jni目錄所有的文件以及需要你手動改動的地方,一個都不能少,一個都不能錯,如果出錯,根據(jù)錯誤提示進行修改。
6開始編譯(最激動人心的時刻)
還是打開Terminal窗口并且切換到j(luò)ni文件目錄下
執(zhí)行命令
ndk-build
等待編譯完成,只要沒有出現(xiàn)stop的情況,基本上沒有任何問題
如果走到這里,那么恭喜你編譯成功,刷新main目錄,會多出2個文件夾分別是lib和obj
lib下的生成了對應(yīng)框架下需要用到的so文件,把armeabi和armeabi-v7a兩個文件夾考到j(luò)niLib或者main下面自己創(chuàng)建的libs文件夾下
我這里是自己創(chuàng)建的lib文件夾,所以在app module下build.gradle文件中新增內(nèi)容
sourceSets {
main {
jniLibs.srcDirs = ['libs']
jni.srcDirs = []
}
}
到這里基本上我們項目中集成ffmpeg相關(guān)的工作已經(jīng)完成,其他框架的so文件大家自己嘗試編譯,有問題再說,后面主要就是android 應(yīng)用端怎么去使用ffmepg。