? ? ? ? ffmpeg使用drawtext濾鏡需要在編譯的時(shí)候使能drawtext,要想成功使能必須要先集成編譯freetype庫(kù),并通知到ffmpeg(交叉編譯沒(méi)有安裝到系統(tǒng)庫(kù)路徑)。
? ? ? ? 也有看到有的文章說(shuō)需要集成fribidi,筆者也交叉編譯了,但是最終沒(méi)有用到,可能是ffmpeg版本的原因。
一、交叉編譯freetype
freetype:下載地址:https://freetype.org/download.html
筆者選擇了紅框鏈接進(jìn)行下載,下載的2.10版本。
(ps:下載的時(shí)候我沒(méi)注意時(shí)間,以為最上面的是最新的【笑哭】)
下載完成后解壓文件(解壓到當(dāng)前文件夾)
tar -zxvf freetype-2.10.0.tar.gz .
接下來(lái)就是交叉編譯了,沒(méi)有交叉編譯環(huán)境的朋友可以參考我之前的文章搭建環(huán)境,傳送門(mén)Android FFMPEG編解碼實(shí)踐(一):Ubuntu 22.04 NDK編譯FFMPEG+libx264_android編譯libx264-CSDN博客
?新建shell腳本文件,文件內(nèi)容如下。筆者使用的是ubuntu的虛擬機(jī),所以工具鏈選擇的是linux-x86_64,其他的大家根據(jù)自己的環(huán)境路徑修改一下DNK的路徑。
#!/bin/bash
export NDK=/home/selivert/ndk/android-ndk-r21e #NDK path
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
export API=21
function build_android
{
APP_ABI=$1
echo "======== > Start build $APP_ABI"
case ${APP_ABI} in
armeabi-v7a)
HOST=armv7a-linux-android
CROSS_PREFIX=$TOOLCHAIN/bin/arm-linux-androideabi-
;;
arm64-v8a)
HOST=aarch64-linux-android
CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android-
;;
esac
./configure \
--prefix=$PREFIX \
--disable-cli \
--enable-shared \
--with-pic \
--enable-static \
--enable-strip \
--host=$HOST
make clean
make -j4
make install
}
PREFIX=`pwd`/android/armeabi-v7a
SYSROOT=$TOOLCHAIN/sysroot
export TARGET=armv7a-linux-androideabi
export CC=$TOOLCHAIN/bin/$TARGET$API-clang
export CXX=$TOOLCHAIN/bin/$TARGET$API-clang++
#export EXTRA_CFLAGS="-O0 -g"
#export EXTRA_CXXFLAGS="-O0 -g"
build_android armeabi-v7a
PREFIX=`pwd`/android/arm64-v8a
export TARGET=aarch64-linux-android
export CC=$TOOLCHAIN/bin/$TARGET$API-clang
export CXX=$TOOLCHAIN/bin/$TARGET$API-clang++
build_android arm64-v8a
上面的腳本同時(shí)編譯了32位與64的庫(kù)。
執(zhí)行腳本,編譯完成后(筆者過(guò)程很順利,一次過(guò)),我們到輸出目錄下查看結(jié)果:
?pkgconfig和庫(kù)文件都有了,編譯完成。將結(jié)果拷貝到外部文件夾,方便ffmpeg集中引用。
筆者放在用戶目錄下的out文件夾中,區(qū)分了32位與64位。
32位目錄結(jié)構(gòu)如下,內(nèi)部包含了fdk-aac x264 x265等第三方開(kāi)源庫(kù)
二、編譯ffmpeg 并使能drawtext濾鏡
ffmpeg資源下載不再贅述,參考之前的文章。筆者使用的是4.4.2版本(這個(gè)版本官網(wǎng)好像找不到了,4.4.3應(yīng)該也沒(méi)問(wèn)題)。
不多說(shuō),直接上編譯腳本 ffmpeg_build_android.sh,注意ndk路徑換成自己的(tag1)。如果不需要編碼功能,注釋掉(tag2)
#!/bin/bash
# 用于編譯android平臺(tái)的腳本
# NDK所在目錄
NDK_PATH=/home/selivert/ndk/android-ndk-r21e # tag1
HOST_PLATFORM=linux-x86_64
# minSdkVersion
API=21
TOOLCHAINS="$NDK_PATH/toolchains/llvm/prebuilt/$HOST_PLATFORM"
SYSROOT="$NDK_PATH/toolchains/llvm/prebuilt/$HOST_PLATFORM/sysroot"
# 生成 -fpic 與位置無(wú)關(guān)的代碼
CFLAG="-D__ANDROID_API__=$API -Os -fPIC -DANDROID "
LDFLAG="-lc -lm -ldl -llog "
# 輸出目錄
PREFIX=`pwd`/android
# 日志輸出目錄
CONFIG_LOG_PATH=${PREFIX}/log
# 公共配置
COMMON_OPTIONS=
# 交叉配置
CONFIGURATION=
build() {
APP_ABI=$1
CFLAGTHIRD=-I/home/selivert/out/$APP_ABI/include
LDFLAGTHIRD=-L/home/selivert/out/$APP_ABI/lib
echo "======== > Start build $APP_ABI"
case ${APP_ABI} in
armeabi-v7a)
ARCH="arm"
CPU="armv7-a"
MARCH="armv7-a"
TARGET=armv7a-linux-androideabi
CC="$TOOLCHAINS/bin/$TARGET$API-clang"
CXX="$TOOLCHAINS/bin/$TARGET$API-clang++"
LD="$TOOLCHAINS/bin/$TARGET$API-clang"
# 交叉編譯工具前綴
CROSS_PREFIX="$TOOLCHAINS/bin/arm-linux-androideabi-"
EXTRA_CFLAGS="$CFLAG -mfloat-abi=softfp -mfpu=vfp -marm -march=$MARCH "
EXTRA_LDFLAGS="$LDFLAG"
EXTRA_OPTIONS="--enable-neon --cpu=$CPU "
;;
arm64-v8a)
ARCH="aarch64"
TARGET=$ARCH-linux-android
CC="$TOOLCHAINS/bin/$TARGET$API-clang"
CXX="$TOOLCHAINS/bin/$TARGET$API-clang++"
LD="$TOOLCHAINS/bin/$TARGET$API-clang"
CROSS_PREFIX="$TOOLCHAINS/bin/$TARGET-"
EXTRA_CFLAGS="$CFLAG"
EXTRA_LDFLAGS="$LDFLAG"
EXTRA_OPTIONS=""
;;
x86)
ARCH="x86"
CPU="i686"
MARCH="i686"
TARGET=i686-linux-android
CC="$TOOLCHAINS/bin/$TARGET$API-clang"
CXX="$TOOLCHAINS/bin/$TARGET$API-clang++"
LD="$TOOLCHAINS/bin/$TARGET$API-clang"
CROSS_PREFIX="$TOOLCHAINS/bin/$TARGET-"
#EXTRA_CFLAGS="$CFLAG -march=$MARCH -mtune=intel -mssse3 -mfpmath=sse -m32"
EXTRA_CFLAGS="$CFLAG -march=$MARCH -mssse3 -mfpmath=sse -m32 "
EXTRA_LDFLAGS="$LDFLAG"
EXTRA_OPTIONS="--cpu=$CPU "
;;
x86_64)
ARCH="x86_64"
CPU="x86-64"
MARCH="x86_64"
TARGET=$ARCH-linux-android
CC="$TOOLCHAINS/bin/$TARGET$API-clang"
CXX="$TOOLCHAINS/bin/$TARGET$API-clang++"
LD="$TOOLCHAINS/bin/$TARGET$API-clang"
CROSS_PREFIX="$TOOLCHAINS/bin/$TARGET-"
#EXTRA_CFLAGS="$CFLAG -march=$CPU -mtune=intel -msse4.2 -mpopcnt -m64"
EXTRA_CFLAGS="$CFLAG -march=$CPU -msse4.2 -mpopcnt -m64 "
EXTRA_LDFLAGS="$LDFLAG"
EXTRA_OPTIONS="--cpu=$CPU "
;;
esac
echo "-------- > Start clean workspace"
make clean
echo "-------- > Start build configuration"
CONFIGURATION="$COMMON_OPTIONS"
CONFIGURATION="$CONFIGURATION --logfile=$CONFIG_LOG_PATH/config_$APP_ABI.log"
CONFIGURATION="$CONFIGURATION --prefix=$PREFIX"
CONFIGURATION="$CONFIGURATION --libdir=$PREFIX/libs/$APP_ABI"
CONFIGURATION="$CONFIGURATION --incdir=$PREFIX/includes/$APP_ABI"
CONFIGURATION="$CONFIGURATION --pkgconfigdir=$PREFIX/pkgconfig/$APP_ABI"
CONFIGURATION="$CONFIGURATION --cross-prefix=$CROSS_PREFIX"
CONFIGURATION="$CONFIGURATION --arch=$ARCH"
CONFIGURATION="$CONFIGURATION --sysroot=$SYSROOT"
CONFIGURATION="$CONFIGURATION --cc=$CC"
CONFIGURATION="$CONFIGURATION --cxx=$CXX"
CONFIGURATION="$CONFIGURATION --ld=$LD"
# nm 和 strip
CONFIGURATION="$CONFIGURATION --nm=$TOOLCHAINS/bin/llvm-nm"
CONFIGURATION="$CONFIGURATION --strip=$TOOLCHAINS/bin/llvm-strip"
CONFIGURATION="$CONFIGURATION $EXTRA_OPTIONS"
echo "-------- > Start config makefile with $CONFIGURATION --extra-cflags=${EXTRA_CFLAGS}${CFLAG264} --extra-ldflags=${EXTRA_LDFLAGS}${LDFLAG264}"
./configure ${CONFIGURATION} \
--pkg-config="pkg-config --static" \
--extra-cflags="$EXTRA_CFLAGS$CFLAGTHIRD" \
--extra-ldflags="$EXTRA_LDFLAGS$LDFLAGTHIRD"
#exit
echo "-------- > Start make $APP_ABI with -j1"
make -j1
echo "-------- > Start install $APP_ABI"
make install
echo "++++++++ > make and install $APP_ABI complete."
}
build_all() {
#配置開(kāi)源協(xié)議聲明
COMMON_OPTIONS="$COMMON_OPTIONS --enable-gpl"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-nonfree"
#目標(biāo)android平臺(tái)
COMMON_OPTIONS="$COMMON_OPTIONS --target-os=android"
#配置動(dòng)態(tài)庫(kù) 靜態(tài)庫(kù)靜
COMMON_OPTIONS="$COMMON_OPTIONS --enable-static"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-shared"
#COMMON_OPTIONS="$COMMON_OPTIONS --disable-static"
#COMMON_OPTIONS="$COMMON_OPTIONS --disable-shared"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-protocols"
#開(kāi)啟交叉編譯
COMMON_OPTIONS="$COMMON_OPTIONS --enable-cross-compile"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-optimizations"
COMMON_OPTIONS="$COMMON_OPTIONS --disable-debug"
#盡可能小
COMMON_OPTIONS="$COMMON_OPTIONS --enable-small"
COMMON_OPTIONS="$COMMON_OPTIONS --disable-doc"
#不要命令(執(zhí)行文件)
COMMON_OPTIONS="$COMMON_OPTIONS --disable-programs" # do not build command line programs
COMMON_OPTIONS="$COMMON_OPTIONS --disable-ffmpeg" # disable ffmpeg build
COMMON_OPTIONS="$COMMON_OPTIONS --disable-ffplay" # disable ffplay build
COMMON_OPTIONS="$COMMON_OPTIONS --disable-ffprobe" # disable ffprobe build
COMMON_OPTIONS="$COMMON_OPTIONS --disable-symver"
#COMMON_OPTIONS="$COMMON_OPTIONS --disable-network"
COMMON_OPTIONS="$COMMON_OPTIONS --disable-x86asm"
COMMON_OPTIONS="$COMMON_OPTIONS --disable-asm"
#啟用
COMMON_OPTIONS="$COMMON_OPTIONS --enable-pthreads"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-mediacodec"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-jni"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-zlib"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-pic"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-libx264" #tag2
COMMON_OPTIONS="$COMMON_OPTIONS --enable-libfdk-aac" #tag2
COMMON_OPTIONS="$COMMON_OPTIONS --enable-libfreetype"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-encoder=libfdk_aac" #tag2
COMMON_OPTIONS="$COMMON_OPTIONS --enable-libx265" #tag2
#COMMON_OPTIONS="$COMMON_OPTIONS --enable-avresample"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-encoder=libx264" #tag2
COMMON_OPTIONS="$COMMON_OPTIONS --enable-muxer=flv"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=mpeg4"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=mjpeg"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=png"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=vorbis"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=opus"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=flac"
COMMON_OPTIONS="$COMMON_OPTIONS --enable-filter=drawtext"
echo "COMMON_OPTIONS=$COMMON_OPTIONS"
echo "PREFIX=$PREFIX"
echo "CONFIG_LOG_PATH=$CONFIG_LOG_PATH"
mkdir -p ${CONFIG_LOG_PATH}
#build "armeabi-v7a"
build "arm64-v8a"
#build "x86"
#build "x86_64"
}
#export PKG_CONFIG_PATH=/home/selivert/out/armeabi-v7a/lib/pkgconfig
echo "-------- Start --------"
build_all
echo "-------- End --------"
需要注意的是,筆者shell腳本的基本功基本等于0,也沒(méi)有將32位與64位分成兩個(gè)腳本,編譯32位與64的庫(kù)時(shí)需要手動(dòng)修改腳本。
編譯32位:
上述腳本注釋build "arm64-v8a",放開(kāi)build "armeabi-v7a"保存后,在命令行執(zhí)行如下命令
export PKG_CONFIG_PATH=/home/selivert/out/armeabi-v7a/lib/pkgconfig
./ffmpeg_build_android.sh
編譯64位:
?上述腳本放開(kāi)build "arm64-v8a", 注釋build "armeabi-v7a"保存后,在命令行執(zhí)行如下命令
export PKG_CONFIG_PATH=/home/selivert/out/arm64-v8a/lib/pkgconfig
./ffmpeg_build_android.sh
編譯完成,到android目錄下,將32位與64位的庫(kù)與頭文件拷貝出來(lái),為android項(xiàng)目集成做準(zhǔn)備。
PS:頭文件拷貝ffmpeg的即可,庫(kù)文件除了ffmpeg的so文件,還需拷貝依賴的第三方庫(kù)文件(如libfreetype.so)。
三、代碼使用drawtext
????????使用drawtext主要是初始化濾鏡與使用濾鏡。直接上偽代碼。
初始化:需要注意的是字體文件(fontfile)需要給全路徑。
AVFilterGraph * filter_graph = NULL;
AVFilterContext *bufferSinkCtx = NULL;
AVFilterContext *bufferSrcCtx = NULL;
AVFilterContext* filterCtx = NULL;
int initFilter(AVCodecContext * codecContext)
{
char args[512] = {0};
int ret = 0;
int nb_filters = 0;
const AVFilter *buffersrc = avfilter_get_by_name("buffer");
const AVFilter *buffersink = avfilter_get_by_name("buffersink");
AVFilterInOut *outputs = avfilter_inout_alloc();
AVFilterInOut *inputs = avfilter_inout_alloc();
char filters_descr[2048] = {0};
char font[] = {"/system/fonts/NotoSansCJK-Regular.ttc"}; // 繪制文字的字體,安卓系統(tǒng)中文字體(中日韓)
int fontSize = 36; // 繪制文字的大小 pix
int yLocation = codecContext->height - fontSize * 3.5;
sprintf(filters_descr,"drawtext=fontfile='%s':fontcolor=white:fontsize=%d:text='hello ffmpeg':x=(w-text_w)/2:y=%d",font, fontSize, yLocation); // 文字居中在視頻底部
enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE };
filter_graph = avfilter_graph_alloc();
if (!outputs || !inputs || !filter_graph) {
ret = AVERROR(ENOMEM);
LOGE("initFilter ENOMEM \n");
goto end;
}
/* buffer video source: the decoded frames from the decoder will be inserted here. */
sprintf(args,
"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
codecContext->width, codecContext->height, codecContext->pix_fmt,
codecContext->time_base.num, codecContext->time_base.den,
codecContext->sample_aspect_ratio.num, codecContext->sample_aspect_ratio.den);
LOGE("initFilter: %s\n", args);
ret = avfilter_graph_create_filter(&bufferSrcCtx, buffersrc, "in",
args, NULL, filter_graph);
if (ret < 0) {
LOGE("Cannot create buffer source\n");
goto end;
}
/* buffer video sink: to terminate the filter chain. */
ret = avfilter_graph_create_filter(&bufferSinkCtx, buffersink, "out",
NULL, NULL, filter_graph);
if (ret < 0) {
LOGE("Cannot create buffer sink\n");
goto end;
}
ret = av_opt_set_int_list(bufferSinkCtx, "pix_fmts", pix_fmts,
AV_PIX_FMT_YUV420P, AV_OPT_SEARCH_CHILDREN);
if (ret < 0) {
LOGE("Cannot set output pixel format\n");
goto end;
}
/* Endpoints for the filter graph. */
outputs->name = av_strdup("in");
outputs->filter_ctx = bufferSrcCtx;
outputs->pad_idx = 0;
outputs->next = NULL;
inputs->name = av_strdup("out");
inputs->filter_ctx = bufferSinkCtx;
inputs->pad_idx = 0;
inputs->next = NULL;
nb_filters = filter_graph->nb_filters;
if ((ret = avfilter_graph_parse_ptr(filter_graph, filters_descr,
&inputs, &outputs, NULL)) < 0) {
LOGE("avfilter_graph_parse_ptr failed %s %x\n", filters_descr, AVERROR(ret));
goto end;
}
filterCtx = filter_graph->filters[nb_filters];
if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0) {
LOGE("avfilter_graph_config failed\n");
goto end;
}
return ret;
end:
avfilter_inout_free(&inputs);
avfilter_inout_free(&outputs);
avfilter_graph_free(&filter_graph);
filter_graph = NULL;
return ret;
}
視頻內(nèi)容繪制文字:
...
// picFrame 存放視頻數(shù)據(jù)的frame,像素格式為yuv420
//添加字幕
int ret = -1;
if (filter_graph && openDrawText) {
ret = av_buffersrc_add_frame_flags(bufferSrcCtx, picFrame, AV_BUFFERSRC_FLAG_PUSH);
if (ret < 0) {
LOGE("Error while feeding the filtergraph ret:%x\n", AVERROR(ret));
}
}
if (ret < 0) {
... // 后續(xù)邏輯,如編碼,渲染
} else {
index++;
if (filterCtx && index == 200) {
// 修改繪制的文字
int ft = av_opt_set_int(filterCtx->priv, "fontsize", 18, 0 );
LOGE("=========== av_opt_set fontsize ret:%x \n", AVERROR(ft));
av_opt_set(filterCtx->priv, "text", "勝多負(fù)少多發(fā)發(fā)所發(fā)一所大所大所大所大所二大所大所大所大所大三所大所大所大所大所四大所大所大\n所大所水五電費(fèi)水電費(fèi)水電費(fèi)水電費(fèi)水電費(fèi)水電費(fèi)", 0 );
}
AVFrame* filterFrame = av_frame_alloc();
/* pull filtered frames from the filtergraph */
while (1) {
ret = av_buffersink_get_frame(bufferSinkCtx, filterFrame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
break;
if (ret < 0) {
LOGE("Error while get the filtergraph error:%x\n", AVERROR(ret));
break;
}
... // 后續(xù)邏輯,如編碼,渲染
}
av_frame_free(&filterFrame);
}
上效果圖:(圖像數(shù)據(jù)是全0的yuv420數(shù)據(jù),所以是綠的)
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-799241.html
?文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-799241.html
到了這里,關(guān)于FFMPEG使用DrawText濾鏡添加字幕,包含ndk編譯freetype的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!