目录
2. Linux中执行bash脚本报错/bin/bash^M: bad interpreter: No such file or directory
编译环境:ubantu 21
ndk版本:r25b
ffmepg版本:4.2.1
环境
下载配置ndk工具
下载
NDK 下载 | Android NDK | Android Developers (google.cn)
解压
unzip android-ndk-r25b-linux-x86_64.zip
配置ndk环境变量
# 打开配置文件
vim /etc/profile
# 保存退出,更新一下环境变量
source /etc/profile
# 测试ndk环境是否配置成功
ndk-build
如果不 是显示“ndk-build not found”, 则说明你的ndk安装成功
下载ffmpeg库并解压
下载地址: http://ffmpeg.org/releases/ffmpeg-4.2.1.tar.bz2
#解压
tar -jxvf ffmpeg-4.2.1.tar.bz2
解压完成后,进入ffmpeg目录,修改configure文件。 在此文件,你可以选择要编译的模块。
由于编译出来的动态库文件名的版本号在.so之后(例如 “libavcodec.so.5.100.1”),而Android平台不能识别这样的文件名,所以需要修改这种文件名
# 跳到3717行,原来的变量为
#SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)'
#LIB_INSTALL_EXTRA_CMD='$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"'
#SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'
#SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR) $(SLIBNAME)'
#将其修改成
SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'
SLIB_INSTALL_LINKS='$(SLIBNAME)'
编写编译脚本
#!/bin/bash
NDK=/root/android/android-ndk-r25b
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64/
API=29
function build_android
{
echo "Compiling FFmpeg for $CPU"
./configure \
--prefix=$PREFIX \
--enable-neon \
--enable-hwaccels \
--enable-gpl \
--enable-postproc \
--enable-shared \
--enable-jni \
--disable-mediacodec \
--disable-decoder=h264_mediacodec \
--disable-static \
--disable-doc \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-avdevice \
--disable-doc \
--disable-symver \
--cross-prefix=$CROSS_PREFIX \
--target-os=android \
--arch=$ARCH \
--cpu=$CPU \
--cc=$CC
--cxx=$CXX
--enable-cross-compile \
--sysroot=$SYSROOT \
--extra-cflags="-Os -fpic $OPTIMIZE_CFLAGS" \
--extra-ldflags="$ADDI_LDFLAGS" \
$ADDITIONAL_CONFIGURE_FLAG
make clean
make
make install
echo "The Compilation of FFmpeg for $CPU is completed"
}
#armv8-a
ARCH=arm64
CPU=armv8-a
CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang
CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++
SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/llvm-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-march=$CPU"
build_android
#armv7-a
ARCH=arm
CPU=armv7-a
CC=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang
CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang++
SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/llvm-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU "
build_android
#x86
ARCH=x86
CPU=x86
CC=$TOOLCHAIN/bin/i686-linux-android$API-clang
CXX=$TOOLCHAIN/bin/i686-linux-android$API-clang++
SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/llvm-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32"
build_android
#x86_64
ARCH=x86_64
CPU=x86-64
CC=$TOOLCHAIN/bin/x86_64-linux-android$API-clang
CXX=$TOOLCHAIN/bin/x86_64-linux-android$API-clang++
SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/llvm-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-march=$CPU -msse4.2 -mpopcnt -m64 -mtune=intel"
build_android
执行脚本编译ffmpeg
# 进行脚本文件所在目录,执行脚本命令
./build.sh
错误解决方法
1. 编译x86-64时提示安装yasm
FFmpeg yasm/nasm not found or too old. Use --disable-yasm for a crippledbuild
yasm是汇编编译器,ffmpeg为了提高效率使用了汇编指令,如MMX和SSE等。
所以系统中未安装yasm时,就会报上面错误
解决:
# 安装最新的yasm
sudo apt-get install yasm
#或者不启用yasm,脚本里加上配置
./configure --disable-x86asm
2. Linux中执行bash脚本报错/bin/bash^M: bad interpreter: No such file or directory
原因:在windows上编译脚本文件,由于dos格式的文件行尾为^M$,unix格式的文件行尾为$,产生错误
解决:
#查看
cat -A xxx.sh
#替换
sed -i "s/\r//" build.sh
3. so链接错误
UnsatisfiedLinkError: dlopen failed: cannot locate symbol "ff_vp9_copy128_aarch64"
解决:编译时打开neon
--enable-neon \
把编译好的库加入项目中
编写cmakelist.txt文件
cmake_minimum_required(VERSION 3.22.1)
project("myplayer")
# 引入ffmpeg文件
include_directories(include)
# 引入avcodec
add_library(avcodec SHARED IMPORTED)
set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/libs/${CMAKE_ANDROID_ARCH_ABI}/libavcodec.so)
# 引入avfilter
add_library(avfilter SHARED IMPORTED)
set_target_properties(avfilter PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/libs/${CMAKE_ANDROID_ARCH_ABI}/libavfilter.so)
# 引入avformat
add_library(avformat SHARED IMPORTED)
set_target_properties(avformat PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/libs/${CMAKE_ANDROID_ARCH_ABI}/libavformat.so)
# 引入avutil
add_library(avutil SHARED IMPORTED)
set_target_properties(avutil PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/libs/${CMAKE_ANDROID_ARCH_ABI}/libavutil.so)
# 引入postproc
add_library(postproc SHARED IMPORTED)
set_target_properties(postproc PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/libs/${CMAKE_ANDROID_ARCH_ABI}/libpostproc.so)
# 引入swresample
add_library(swresample SHARED IMPORTED)
set_target_properties(swresample PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/libs/${CMAKE_ANDROID_ARCH_ABI}/libswresample.so)
# 引入swscale
add_library(swscale SHARED IMPORTED)
set_target_properties(swscale PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/libs/${CMAKE_ANDROID_ARCH_ABI}/libswscale.so)
add_library(${CMAKE_PROJECT_NAME} SHARED
native-lib.cpp)
target_link_libraries(
${CMAKE_PROJECT_NAME}
avcodec
avfilter
avformat
avutil
postproc
swresample
swscale
android
log)
目录结构
在工程build.gradle文件中,配置so库的目录
sourceSets {
main {
jni {
srcDirs 'src\\main\\cpp\\libs'
}
}
}
测试ffmpeg是否加入成功
#include <jni.h>
#include <string>
#include "Log.h"
extern "C" {
#include <libavutil/avutil.h>
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_myplayer_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
int v = avutil_version();
LOGE("avutil_version()=%d", v);
return env->NewStringUTF(hello.c_str());
}