Skip to content

linux版本 ​

项目源码: https://github.com/litongjava/java-native-media


一、问题现象 ​

应用启动时报错:

os name: linux user.home: /root
Detected FFmpeg ABI: 58
copy from /lib/linux_amd64/libnative_media_av58.so to /root/lib/linux_amd64/libnative_media_av58.so
Resource does not exist: /lib/linux_amd64/libnative_media_av58.so
Loading native: /root/lib/linux_amd64/libnative_media_av58.so

Exception in thread "main" java.lang.reflect.InvocationTargetException
Caused by: java.lang.UnsatisfiedLinkError: Can't load library: /root/lib/linux_amd64/libnative_media_av58.so

二、问题根因 ​

1)应用内置的 native 库依赖 FFmpeg 59 ​

检查 .so 依赖:

bash
readelf -d /root/lib/linux_amd64/libnative_media_av59.so | grep NEEDED

输出:

Shared library: [libavformat.so.59]
Shared library: [libavcodec.so.59]
Shared library: [libswresample.so.4]
Shared library: [libswscale.so.6]
Shared library: [libavutil.so.57]
Shared library: [libavfilter.so.8]

说明该库是 按 FFmpeg 5.x (ABI 59) 编译的


2)服务器实际安装的是 FFmpeg 4.x(ABI 58) ​

bash
ldconfig -p | grep libavformat

输出:

libavformat.so.58 => /lib/x86_64-linux-gnu/libavformat.so.58

3)ldd 验证动态链接失败 ​

bash
ldd /root/lib/linux_amd64/libnative_media_av59.so

输出:

libavformat.so.59 => not found
libavcodec.so.59 => not found
libswresample.so.4 => not found
libswscale.so.6 => not found
libavutil.so.57 => not found
libavfilter.so.8 => not found

结论 ​

项目版本
native 库FFmpeg 59
系统 FFmpegFFmpeg 58
结果动态链接失败

因此 JVM 无法加载 .so 文件,触发:

java.lang.UnsatisfiedLinkError

三、java-native-media 的加载机制 ​

库启动时会:

  1. 检测系统 FFmpeg ABI
  2. 自动选择对应 native 库

日志:

Detected FFmpeg ABI: 58
Loading native: libnative_media_av58.so

但 jar 包内只带 av59,没有 av58:

Resource does not exist: /lib/linux_amd64/libnative_media_av58.so

因此加载失败。


四、解决方案 ​

有三种方案:

方案推荐度说明
升级系统 FFmpeg 到 59⭐⭐影响系统组件
静态链接 FFmpeg⭐⭐编译复杂
按服务器版本编译 native⭐⭐⭐⭐最安全

推荐:为服务器编译 av58 版本 native 库


五、编译适配服务器的 native 库 ​

1)准备环境 ​

Ubuntu / Debian:

bash
sudo apt install build-essential -y
sudo apt-get install nasm -y
sudo apt-get install pkg-config -y
sudo apt install cmake -y
sudo apt-get install libavcodec-dev libavformat-dev libavutil-dev libavfilter-dev libswresample-dev libmp3lame-dev -y

确认版本:

bash
ffmpeg -version

应包含:

libavformat 58.x

安装java

sh
mkdir /opt/package/java -p &&cd /opt/package/java 
wget https://github.com/litongjava/oracle-jdk/releases/download/8u411/jdk-8u411-linux-x64.tar.gz
wget https://gitcode.com/ppnt/oracle-jdk/releases/download/8u411/jdk-8u411-linux-x64.tar.gz
mkdir /usr/java/ -p
tar -xf jdk-8u411-linux-x64.tar.gz -C /usr/java
export JAVA_HOME=/usr/java/jdk1.8.0_411
export JAVA_HOME=/usr/java/jdk1.8.0_411

2)下载源码 ​

bash
git clone https://github.com/litongjava/native-media.git
cd native-media

3)编译 ​

编译

bash
cmake -S . -B cmake-build-release -DCMAKE_BUILD_TYPE=Release
cmake --build cmake-build-release --target all

编译成功后生成:

libnative_media.so

六、部署 native 库 ​

创建运行目录:

bash
mkdir -p /root/lib/linux_amd64/

复制编译产物:

bash
cp cmake-build-release/libnative_media.so /root/lib/linux_amd64/libnative_media_av58.so

权限:

bash
chmod 755 /root/lib/linux_amd64/libnative_media_av58.so

七、验证 ​

重新启动服务:

Detected FFmpeg ABI: 58
Loading native: /root/lib/linux_amd64/libnative_media_av58.so

若无 UnsatisfiedLinkError,说明成功。


八、原理总结 ​

为什么不能跨 ABI 使用? ​

FFmpeg 每个大版本都会改变:

  • 结构体大小
  • 函数签名
  • 枚举值
  • 内存布局

例如:

ABIFFmpeg
57FFmpeg 3
58FFmpeg 4
59FFmpeg 5
60FFmpeg 6

native 库是 强绑定 ABI 的

因此:

libnative_media_av59.so ≠ 可在 ffmpeg58 上运行

九、最佳实践建议 ​

生产环境建议:

1)构建阶段与运行环境一致 ​

Docker 构建示例:

构建机 FFmpeg版本 == 服务器 FFmpeg版本

否则必然出现 JNI 加载失败。


2)将 native 编译纳入 CI ​

建议:

CI 为每种 Linux 发行版编译对应 so

例如:

libnative_media_av58_ubuntu20.so
libnative_media_av59_ubuntu22.so

3)不要依赖 jar 内置 native ​

原因:

  • Linux 发行版 FFmpeg 版本差异极大
  • 云服务器经常是旧版 LTS

应改为:

启动时优先加载外部 native

MIT Licensed