1. 程式人生 > >Ubuntu下用NDK編譯移植FFmpeg 2.0(配置最新版x264)到Android平臺

Ubuntu下用NDK編譯移植FFmpeg 2.0(配置最新版x264)到Android平臺


將x264配置到ffmpeg中需要先編譯x264,生成靜態庫或動態庫。因為264的靜態庫本身不大(我編譯完成後是1.1M)且考慮到平臺移植問題,這裡選擇的是編譯生成靜態庫。

準備,新建工作空間

(1)建立總目錄FFmpeg-Android

eg: mkdir workspace --> cd workspace --> mkdir FFmpeg-Android --> cd FFmpeg-Android

(2)建立儲存x264靜態庫的目錄 android-x264

(在FFmpeg-Android目錄下)mkdir 264 --> cd 264

1、編譯x264

(1)去官網:
http://www.videolan.org/developers/x264.html
 下載最新的264原始碼。這裡下載的是2013年10月份出的版本0.14。將壓縮包解壓縮到264FFmpeg-Android/264目錄下,

(2)編寫指令碼檔案:

export NDK=$NDK_HOME
export PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.6/prebuilt
export PLATFORM=$NDK/platforms/android-19/arch-arm
export PREFIX=../../android-x264

./configure --prefix=$PREFIX \
--enable-static \
--enable-pic \
--disable-asm \
--disable-cli \
--host=arm-linux \
--cross-prefix=$PREBUILT/linux-x86_64/bin/arm-linux-androideabi- \
--sysroot=$PLATFORM

注意:這裡涉及到路徑的變數需要根據你們自己的情況來調整。

PREBUILT 變數儲存的是NDK交叉編譯鏈的路徑,

PLATFORM 變數儲存的是NDK進行連結時查詢庫檔案的路徑(arch-arm目錄下儲存的是各種andorid平臺下的c庫標頭檔案和靜態以及動態庫)

PREFIX 變數儲存的是編譯生成的靜態庫儲存的路徑

(3)執行指令碼 

(4)執行完剛才的指令碼後,會在當前目錄生成配置檔案,這裡還需要修改以下幾個檔案,原因是:編譯成功後在eclipse下執行會出現 "cannot locate symbol 'log2f' " 的錯誤

修改1、config.h

---- #define HAVE_LOG2F 1

+++#define HAVE_LOG2F 0

修改2、encoder/encoder.c 、 encoder/ratecontrol.c 、encoder/analyse.c 、encoder/set.c

在檔案開頭處新增

+++ #include 

+++ //

(5)在當前目錄下執行: make , make install

完成後,就可以在FFmpeg-Android的android-x264目錄下生成標頭檔案和靜態庫檔案了,至此,x264編譯完成。

2、編譯FFmpeg 2.0 ,下載地址:
http://ffmpeg.org/olddownload.html


(1)將壓縮包解壓縮到FFmpeg-Android的根目錄下

(2)編寫指令碼,這裡將配置和編譯過程分成兩個指令碼來寫,主要是配置指令碼完成後,還要手動的去修改ffmpeg目錄下的config.h檔案

配置指令碼:

#!/bin/bash

DEST=`pwd`/build/ffmpeg && rm -rf $DEST
SOURCE=`pwd`/ffmpeg


if [ -d ffmpeg ]; then
cd ffmpeg
else
echo "can not find ffmpeg source code"
exit 1
fi

if [ "$PIPESTATUS" != "0" ] ; then
echo "error in FFmpeg-Android.sh : 14 "
fi


TOOLCHAIN=/tmp/vplayer
SYSROOT=$TOOLCHAIN/sysroot/
$NDK_HOME/build/tools/make-standalone-toolchain.sh --platform=android-19 --install-dir=$TOOLCHAIN

export PATH=$TOOLCHAIN/bin:$PATH
export CC="arm-linux-androideabi-gcc"
export LD=arm-linux-androideabi-ld
export AR=arm-linux-androideabi-ar

#CFLAGS="-O3 -Wall -mthumb -pipe -fpic -fasm \
# -finline-limit=300 -ffast-math \
# -fstrict-aliasing \
# -fmodulo-sched -fmodulo-sched-allow-regmoves \
# -Wno-psabi -Wa,--noexecstack \
# -DANDROID -DNDEBUG"

CFLAGS="-Os -fPIC -marm"

FFMPEG_FLAGS="--target-os=linux \
--arch=arm \
--sysroot=$SYSROOT \
--enable-cross-compile \
--cross-prefix=arm-linux-androideabi- \
--enable-shared \
--enable-static \
--disable-symver \
--disable-doc \
--disable-ffplay \
--disable-ffmpeg \
--disable-ffprobe \
--disable-ffserver \
--disable-avdevice \
--disable-avfilter \
--disable-filters \
--disable-devices \
--disable-pthreads \
--disable-everything \
--enable-gpl \
--enable-muxers \
--enable-encoders \
--enable-protocols \
--enable-parsers \
--enable-demuxers \
--enable-decoders \
--enable-bsfs \
--enable-network \
--enable-swscale \
--enable-libx264 \
--enable-encoder=libx264 \
--enable-decoder=h264 \
--enable-muxer=h264 \
--enable-demuxer=h264 \
--disable-demuxer=sbg \
--disable-demuxer=dts \
--disable-parser=dca \
--disable-decoder=dca \
--extra-libs=-lx264 \
--enable-asm \
--enable-version3"

VERSION=armv7
cd $SOURCE

EXTRA_CFLAGS="-I../android-x264/include -march=armv7-a"
EXTRA_LDFLAGS="-L../android-x264/lib"

PREFIX="$DEST/$VERSION" && mkdir -p $PREFIX
FFMPEG_FLAGS="$FFMPEG_FLAGS --prefix=$PREFIX"

sh ./configure $FFMPEG_FLAGS --extra-cflags="$CFLAGS $EXTRA_CFLAGS" --extra-ldflags="$EXTRA_LDFLAGS" | tee $PREFIX/configuration.txt
if [ "$?" = "0" ] ; then
echo "./configure success in shell-script"
else
echo "./configure erroe in shell-script"
exit 1
fi
cp config.* $PREFIX

我們編譯ffmpeg的過程是這樣的,將交叉編譯鏈拷貝整個拷貝一份到系統的臨時目錄下/tmp的vplayer中(交叉編譯鏈的結構其實蠻有意思,我在另一篇部落格中會好好分析)

DEST , 生成的ffmpeg共享庫libffmpeg.so儲存的路徑

SOURCE, ffmpeg原始碼的路徑

TOOLCHAIN ,用來儲存交叉編譯鏈的目錄路徑

SYSROOT , 用來儲存交叉編譯練進行連結步驟時查詢庫檔案的路徑

EXTRA_CFLAGS,用來幫助我們將264整合到ffmpeg時,查詢264標頭檔案的路徑

EXTRA_LDFLAGS,ffmpeg查詢264靜態庫的路徑

其他的變數估計都是見名知意的了,不明白的可以給我留言。

OK,編寫完指令碼後,先執行 config-ffmpeg-Android.sh 指令碼,對ffmpeg進行配置

PS. 如果執行配置指令碼時,遇到諸如 ” c compiler failed ....“ 之類的錯誤,多半是你配置指令碼中某個變數的路徑錯了。

配置指令碼執行完後,會打印出ffmpeg的配置資訊

從encoders 或者decoders中可以看出來,我們的ffmepg已經成功配置上了264

編譯完成後,會出現一個警告,這個可以忽略。

(3)配置指令碼config-ffmpeg-andorid.sh 執行完後,還需要修改ffmpeg目錄下的config.h檔案,同樣是修改 HAVE_LOGx的巨集值

----#define HAVE_LOG2 1

----#define HAVE_LOG2F 1

----#define HAVE_LOG10F 1

+++#define HAVE_LOG2 0

+++#define HAVE_LOG2F 0

+++#define HAVE_LOG10F 0

(4)修改完成後,執行編譯指令碼 ./make-ffmpeg-Android.sh 指令碼,指令碼內容如下:

#!/bin/bash

DEST=`pwd`/build/ffmpeg && rm -rf $DEST
SOURCE=`pwd`/ffmpeg

TOOLCHAIN=/tmp/vplayer
SYSROOT=$TOOLCHAIN/sysroot/

export PATH=$TOOLCHAIN/bin:$PATH
export CC="arm-linux-androideabi-gcc"
export LD=arm-linux-androideabi-ld
export AR=arm-linux-androideabi-ar

CFLAGS="-Os -fPIC -marm"

VERSION=armv7
cd $SOURCE

PREFIX="$DEST/$VERSION" && mkdir -p $PREFIX


EXTRA_LDFLAGS="-L../android-x264/lib"

make clean
make -j4 || exit 1
make install || exit 1

$CC -llog -lm -lz -shared --sysroot=$SYSROOT -Wl,--no-undefined -Wl,-z,noexecstack $EXTRA_LDFLAGS libavutil/*.o libavutil/arm/*.o libavcodec/*.o libavcodec/arm/*.o libavformat/*.o libswresample/*.o libswscale/*.o -o $PREFIX/libffmpeg.so -lx264 -L/home/yinjingyu/workspace/lab-project/ffmpegcpl/FFmpeg-Android/lib/-lx264

cp $PREFIX/libffmpeg.so $PREFIX/libffmpeg-debug.so

arm-linux-androideabi-strip --strip-unneeded $PREFIX/libffmpeg.so

echo "build successfully!"

注意,下載下來的ffmpeg原始碼需要修改一些原始檔才能順利編譯通過,不過這裡為了方便給大家貼出來錯誤資訊提供參考,我直接執行編譯指令碼,編譯過程出現錯誤 

分析錯誤原因是型別衝突,具體我也沒深究,直接想了個簡單辦法:

首先要知道 strtod 是C庫函式,用來實現將字串轉化成double型別的資料。ffmpeg可能自己又重新實現了一套自己的函式叫做:avpriv_strtod,兩種實現導致了型別衝突。

我的解決方法就是註釋掉ffmpeg自己的實現,直接使用c庫的函式。具體步驟如下:

步驟1:進入我們交叉編譯鏈的目錄,如果你的配置指令碼和我一樣,那就應該是 /tmp/vplayer(之前我們專門拷貝了一份編譯鏈到該目錄下)。在該目錄下有一個sysroot目錄(用來儲存連結時用到的標頭檔案和庫),進入sysroot --> include 下,用vi 開啟 stdlib.h 檔案,新增如下內容:

+++//add by chance_yin 2013.12.27
+++#undef avpriv_strtod
+++#undef strtod
+++#define strtod strtod

步驟2:註釋掉 ffmpeg/compat/strtod.c中的所有程式碼

步驟3:在如下檔案: libavutil/eval.c、libavformat/rtmpproto.c 中的開頭位置新增如下程式碼,

#include 

#undef avpriv_strtod

#undef strtod

#define strtod strtod

步驟4:上面的檔案修改完成後,再次執行編譯指令碼,又出現錯誤:

由於實驗室的專案用不到語音,所以對於錯誤:swri_audio_convert_init_arm,我的處理辦法是直接註釋掉,

剩下的關於 log2_tab.o multiple definition 的錯誤,就好解決了,

用vi 開啟: libavcodec/Makefile 、libavformat/Makefile 、libswresample/Makefile 檔案,將其中的

OBJS-$(CONFIG_SHARED) += log2_tab.o 註釋掉即可。

好了,原始碼的編譯算是告以段落了,貼出來編譯成功的圖,然後你就可以在FFmpeg-android/build/ffmpeg/armv7目錄下找到編譯完成的 libffmpeg.so 檔案了

結束:

之前實驗室專案一直用的是ffmpeg 0.x的版本,昨天把2.0版本的libffmpeg.so整合到專案中發現原來的程式碼在 avcodec_open2 報錯,在ffmpeg相應原始檔中打了log才發現是2.0之後,ffmpeg棄用了很多巨集。

專案老版本的程式執行到 avcodec_open2 出錯, 在原始碼此方法的實現中打log,

發現停在了“specific pixfmt not supported 。。。”,google一下,發現在ffmpeg2.0版本中,ffmpeg不再支援pCodecCtxEnc->pix_fmt = PIX_FMT_YUV420P;只要把該值設定成pCodecCtxEnc->pix_fmt = AV_PIX_FMT_YUV420P; 就可以了。

作者:chance_yin