天天看點

一文讓你發現原來FFmpeg這麼有意思

前言

其實FFmpeg大家都耳熟能詳的的一個東西了,知名的ijkplayer 就是有Bilibili開源出來的播放器也是基于FFmpeg做出來的。但是FFmpeg不僅僅止于音視訊的播放裁剪功能,還能幹些更有趣的事情,就比如說視訊的畫面拼接,對于一個Android開發者來說,一想到FFmpeg c/c++是跑不掉的。下載下傳官方庫,然後編譯成不同平台的so,然後在通過jni內建到項目中,這一個開頭就難倒了不少開發者。一個問題是如何編譯出這個so。如何把編譯好的檔案內建到項目中。這個我之後的文章會詳細說明,這裡我隻和大家說說有趣在哪裡。

說道這裡 我不得不說,現在各個大廠都在做統一前端的開發語言和架構,搞得開發者TMD頭都大,其實不還是做得都是一些UI頁面而已,通過不同的方式渲染而已,如果真的要做一些定制化的功能,還是離開不了原生。

開始之前已經給大家準備好了安裝環境和各種視訊資料帶你入門FFmpeg,資料放在自己的群裡面:832218493(需要自取)

一文讓你發現原來FFmpeg這麼有意思
一文讓你發現原來FFmpeg這麼有意思

Android FFmpeg視訊畫面拼接

通過标題大家可能還是不太明白啥意思,我就上幾張我做的這個App的截圖吧,順便放幾張動态圖,可能大家能知道個大概功能。其實大家看了之後,可能感覺感覺市面上已經有做到了這種效果的App了,但是作為一個Android攻城獅,必須要知道他是怎麼做出來的,在ios上做這種效果很容易,有原生的Api提供,但是苦逼的Android 那隻能通過C/C++來實作,原生搞的話很難。

這個App大概有以下功能(目前并未開源,項目位址)

視訊畫面拼接(主要的)

視訊寬高裁剪

視訊時長裁剪

添加視訊邊框及修改邊框顔色

添加視訊封面

添加視訊背景圖檔

添加視訊水印

添加背景音樂

下面是App做出來的兩個視訊轉gif的圖檔

一文讓你發現原來FFmpeg這麼有意思
一文讓你發現原來FFmpeg這麼有意思

這個app已有下面的頁面,不過現在還沒有開源,還有很多地方要改。功能是實作了。但是頁面還有很多優化的地方,是以啊 FFmpeg真的很牛逼。接下來我會教大家自己怎麼實作這個功能。那麼來看看做出來的視訊如何,某音好像沒有這個功能哦,

一文讓你發現原來FFmpeg這麼有意思
一文讓你發現原來FFmpeg這麼有意思
一文讓你發現原來FFmpeg這麼有意思
一文讓你發現原來FFmpeg這麼有意思

如何實作這種效果

這一期,我隻是教大家如何通過shell 指令實作一個視訊畫面拼接。首先從最基礎的功能來實作吧。然後在根據目前的功能進行擴充。至于FFmpeg庫的編譯,一時半會兒也說不完。參數配置也很麻煩,尤其是不同的平台坑很多。這裡我就以最簡單的方式交大家如何實作吧

第一步

既然要做功能,第一步當然是找相應的工具了,我們可以通過FFmpeg官網找到,對應自己平台的靜态庫,大家别下錯了哈,static binary不要下載下傳錯了,下載下傳成源檔案了。因為官方已經給我們編譯好了靜态庫,可以直接通過shell 腳本就可以簡單的走起來。

FFmpeg官方網站庫

打開連結,我這邊選中的是mac os 可以看到底下提供了 static 庫。我們可以直接下載下傳下來就行了。Ubuntu 和windows 都是一樣的 都有現成的庫。可以下載下傳下來。

一文讓你發現原來FFmpeg這麼有意思

下載下傳下來就是一個這個東西。我這邊大概71M。還挺大的,因為裡面官方給我們把大部分基本的功能都給編譯進去了。是以這麼大,但是如果我們自己編譯的話,會小很多。可以吧一下不需要的東西直接disable

一文讓你發現原來FFmpeg這麼有意思

第二步

那當然是打開我們的terminal,windows 直接win+R 然後cmd就ok,然後cd 到我們下載下傳的ffmpeg靜态庫的檔案路徑,執行一下指令,不出意外的話,這時候你應該可以看到以下頁面

./ffmpeg
           

複制代碼如果出現一下内容,說明這時候你的ffmpeg 已經ok了

一文讓你發現原來FFmpeg這麼有意思

如果說權限不足的話 ,cd到你的靜态庫路徑下執行這個就可以了。加個sudo,然後在change mode 一下 賦予777權限

chmod +x  ffmpeg
chmod 777 ffmpeg
           

複制代碼這時候ffmpeg環境已經準備好了。接下來萬事俱備隻欠東風了。

第三步

視訊拼接,那麼如何視訊拼接呢,不用擔心 ffmpeg 已經給我們做好了相應的封裝了。隻要了解相應的指令就可以完成了最基礎的視訊拼接功能了。其實ffmpeg不僅僅如此啊,他的功能太強大了。

先簡單的來幾個指令看看,檢視視訊檔案資訊

./ffmpeg -i input.mp4
           

複制代碼這時候我們可以看到視訊的具體資訊fps,寬高啊,視訊品質啊,等等很多資訊

一文讓你發現原來FFmpeg這麼有意思

-i 的指令就是入參的,接下來我們直接看如何通過指令把一個視訊合在一起,就比如上面兩個視訊合在一起的小姐姐,我們通過電腦簡單的來合成一下。

./ffmpeg -i input2.mp4  -i input2.mp4  -filter_complex [0][1]vstack -y output.mp4 
           

複制代碼成功的話應該是以下頁面

一文讓你發現原來FFmpeg這麼有意思

這一個MP4檔案是同級目錄下的檔案,大家可以換成自己的檔案名就可以,至于filter_complex嗎?我這邊就大白話講了,就相當于我們java 中的函數一樣,你把輸入給他 在這個方法體内,把相應的輸出給你。

vstack 可以官網看到這樣的介紹 Stack input videos vertically.就是垂直拼接視訊的意思,同理如何想水準的話,隻要把vstack 改成hstack就可以了,老鐵們是不是很簡單,很給力,

最主要的是自己通過指令行就可以完成一些視訊的基本操作,什麼ps,Photoshop,我指令行就ok了。哈哈哈哈哈哈哈哈

剛剛作出來的視訊效果,截圖給大家看看

一文讓你發現原來FFmpeg這麼有意思

編譯Android平台FFmpeg

關于編譯Android 平台的庫可能網上有一大堆,反正一搜尋,肯定有你需要的,這邊我主要教大家使用靜态庫而非動态庫,這樣你會省了很多很多的麻煩,各種so庫的來回粘貼複制,還要寫cmakelist文集,配置gradle,對于沒怎麼玩過FFmpeg的人來說可能需要搞很長時間,這裡我将帶給大家另一種玩法

動态庫

動态庫其實就是編譯出來的so庫,link到我們的項目中然後load library 然後通過jni的方式進行操作c上面的東西,這邊就是簡單概括一下,那我知道了需要哪些東西了,那我們接下來就是,編譯這個so庫,大家可以在網上看到FFmpeg編譯出來的有很多so 你要一個一個的把他們放進我們的項目中。然後cmakelist裡面添加東西。這裡我教大家把這幾個庫編譯到一個so裡面,可以省了你很多麻煩

以上準備都ok的話,這邊就可以執行腳本指令了打開我們下載下傳的FFmpeg壓縮包,可以看到這些目錄結構,我們編譯主要用到的就是configure這個binary。可以在下圖中看到

一文讓你發現原來FFmpeg這麼有意思

腳本我這邊就把一部分代碼貼上去,全部的我放在了github上了(下面這個腳本名稱叫做build_ffmpeg_android.sh,可以在我上面的寫的位址裡找到),我會寫一些注釋在上面

可以看到下面的 代碼中有一個MODULE(主要做一些裡面庫的enabel 和disable,把需要的庫我們編譯進去 不需要的當然是不用了)

GENERAL(主要作用是一些參數的設定和額外的庫添加)和LIB_TYPE(這個就是設定編譯shared還是static的了)靜态庫或者動态庫

function build
{
  pushd ffmpeg
  ./configure                           \
  --logfile=config.log                  \
  --target-os=android                   \
  --prefix=$PREFIX         \
  ${MODULE}                             \
  ${GENERAL}                            \
  ${LIB_TYPE}                           \
  --sysroot=$PLATFORM                   \
  --extra-cflags="-fPIE -fPIC -std=c11" \
  --extra-ldflags="$flags $shared_flag" \

  # essencial for dynamic library
  sed -i -E "s/^(#define HAVE_SYSCTL).*/\1 0/" config.h

  make clean
  make -j$NUM_OF_CORE
  make install

  popd
}

build
           

這個就是GENERAL的參數

GENERAL="                                             \
  --extra-libs="-lgcc"                                \
  --arch=${ARCH}                                      \
  --cc=$PREBUILT/bin/${BIN_PREFIX}-gcc        \
  --cross-prefix=$PREBUILT/bin/${BIN_PREFIX}- \
  --nm=$PREBUILT/bin/${BIN_PREFIX}-nm         \
  --extra-cflags="-I$X264_INCLUDE"    \
  --extra-ldflags="-L$X264_LIB"       \
"
           

這個是module的參數 隻放了一部分,用到的可以enable 用不到的disable 不然編譯出來的庫很大。那我們的apk也會相應的很大,可以到github下載下傳原始檔案

MODULE="                 \
  --enable-jni           \
  --enable-pic           \
  --enable-gpl           \
  --enable-zlib          \
  --enable-yasm          \
  --enable-small         \
  --enable-pthreads      \
  --enable-mediacodec    \
   --enable-libx264      \
  --enable-cross-compile \
  --disable-doc          \
  --disable-ffplay       \
  --disable-ffprobe      \
  --disable-network      \
  --enable-neon
  --disable-linux-perf   \
--enable-encoder=libx264 \
--enable-encoder=aac \
--enable-encoder=mpeg4 \
--enable-encoder=mjpeg \
--enable-encoder=png \
--disable-muxers \
           

靜态庫

這邊腳本裡寫了一些判斷,我們可以執行腳本的時候 再加個字段就可以編譯出我們需要的靜态庫,我這邊的腳本名字叫做build_ffmpeg_android.sh,是以隻要按照下面的指令執行即可

複制代碼如果你想編譯動态庫 隻要把static 改成shared即可。

現在的電腦應該編譯的很快,執行成功應該可以看到下面的目錄,so已經編譯出來了

一文讓你發現原來FFmpeg這麼有意思

靜态庫已經出來了

把編譯出來的庫合成一個so庫

隻要把以下的代碼添加到編譯腳本裡面即可。

$TOOLCHAIN/bin/${BIN_PREFIX}-ld \
      -rpath-link=$PLATFORM/usr/lib \
      -L$PLATFORM/usr/lib \
      -L$PREFIX/lib \
      -L$X264_LIB \
      -soname libffmpeg.so -shared -nostdlib -Bsymbolic --whole-archive --no-undefined -o \
      $PREFIX/libffmpeg.so \
      $PREFIX/lib/libavcodec.a \
      $PREFIX/lib/libavfilter.a \
      $PREFIX/lib/libavformat.a \
      $PREFIX/lib/libavutil.a \
      $PREFIX/lib/libswresample.a \
      $PREFIX/lib/libswscale.a \
      $X264_ALIB/libx264.a \
      -lc -lm -lz -ldl -llog --dynamic-linker=/system/bin/linker \
      $TOOLCHAIN/lib/gcc/${BIN_PREFIX}/4.9.x/libgcc.a
           

從上面腳本可以看到 相當于把這些庫linker到我們上面的libffmpeg.so裡面。

成功的話可以看到ffmpeg 目錄下的Android中看到這個so庫

可以看到libffmpeg.so已經出來了

一文讓你發現原來FFmpeg這麼有意思

編譯Lib264庫

作用

為什麼用這個庫呢,如果說你已經以上步驟都成功了,而且已經運作到Android機上面了,你會發現編碼出來的視訊檔案明顯品質很差,不應該說很差,反正肯定是自己不滿意的結果。說了這麼多,大家應該知道這個庫的作用了,提高編碼品質,為什麼我在官網下載下傳的pc庫會品質很好呢,那是因為他們已經把這個庫編進去了而且已經enable。那麼我們這裡要做的就是去下載下傳Lib264官方源碼,編譯出Android平台的 然後把這個庫給打進FFmpeg裡面。

編譯Lib264

這個庫編譯就比較簡單了。參數和代碼沒有那麼多,github上面放的腳本名字叫做(build_x264_andorid.sh)大家下載下傳下來就可以用的

如果想編譯不同的版本同樣可以通過 字尾shared 或者static 就可以了

LIB_TYPE=${1-static}
echo '@@@#####'${LIB_TYPE}
function build
{
  pushd x264

  # remove suffix of libx264.so
  sed -in 's/so\.\$API/so/g' configure
  ./configure                                           \
  --prefix=./android/$ABI                               \
  --enable-$LIB_TYPE                                    \
  --enable-pic                                          \
  --host=$BIN_PREFIX                                  \
  --cross-prefix=${PREBUILT}/bin/${BIN_PREFIX}- \
  --extra-cflags="-fPIC -fPIE -std=c11"                 \
  --sysroot=$PLATFORM

  sed -i 's/-f -s/-f/g' ./Makefile

  make clean
  make -j$NUM_OF_CORE
  make install
  tree android
  popd
}

build

printf "$success" "x264"
           
./build_x264_android.sh shared
           

執行成功應該可以看到下面的目錄在x264/android/目錄下,so已經編譯出來了

一文讓你發現原來FFmpeg這麼有意思

FFmpeg Lib264合成

上面已經把每個平台的庫都編譯好了,那我們怎麼把這兩個庫合成在一起呢,細心的同學已經看到了,我上面貼腳本的時候已經把代碼貼進去了,就是在我們編譯腳本build_ffmpeg_android.sh的時候已經帶進去了就是那個GENERAL字段

看看下面的字段cflags 和 ldflags 已經把我們之前編譯的x264編譯進去了。

X264_INCLUDE=../x264/android/$ABI/include
X264_LIB=../x264/android/$ABI/lib
X264_ALIB=../x264

GENERAL="                                             \
  --extra-libs="-lgcc"                                \
  --arch=${ARCH}                                      \
  --cc=$PREBUILT/bin/${BIN_PREFIX}-gcc        \
  --cross-prefix=$PREBUILT/bin/${BIN_PREFIX}- \
  --nm=$PREBUILT/bin/${BIN_PREFIX}-nm         \
  --extra-cflags="-I$X264_INCLUDE"    \
  --extra-ldflags="-L$X264_LIB"       \
"
           

編譯不同ARCH庫(armeabi-v7a arm64-v8a…)

這個就比較簡單了,既然一個平台已經成功,那麼其他的改一下

編譯平台不就行了。可以在我們的腳本中修改一些參數即可,

我這邊做了一些判斷可以在編譯腳本前,在我們的common.sh目錄下修改以下ARCH既可以,然後在執行build_ffmpeg_android.sh即可。

#ARCH=arm
ARCH=aarch64
[[ $ARCH = "arm" ]] && \
    { BIN_PREFIX=arm-linux-androideabi; ABI=armeabi-v7a; } ||     { BIN_PREFIX=aarch64-linux-android; ABI=arm64-v8a; }

NDK=~/Downloads/android-ndk-r14b
PLATFORM=$NDK/platforms/android-21/arch-arm64
PREBUILT=$NDK/toolchains/${BIN_PREFIX}-4.9/prebuilt/linux-x86_64
TOOLCHAIN=$NDK/toolchains/${BIN_PREFIX}-4.9/prebuilt/linux-x86_64
NUM_OF_CORE=$(nproc)
success="${BLACKB}${YELLOWF}build %s success${RESET}\n"
           

以上就是common.sh腳本。隻要修改上面的arch參數就行 ,如果要變異arm的話記得把PLATFORM這個參數後面的64去了。

收尾

可能上面有說的不清楚的。大家可以在留言中或者私信中 提問。如果上面有說錯的地方,大家可以積極的和我說。我會在文章中糾正。

To be continue… 下回我會帶給大家 關于App的一些内容。比如畫面拼接,添加logo,添加背景音樂,視訊畫面剪切…有想要了解的也可以在留言中提及。我會做相關方面的調研,