注:笔者服务器系统为Ubuntu 22.04 linux-x86_64,其他操作系统或许可参考于该博客,但笔者也没有完全试过
在笔者之前的专栏博客中,已经完成ubuntu系统中编译安装ffmpeg集成qsv,实现硬件加速解码;实际应用过程中,使用javacv进行推流、解码、抽帧等,若要将硬件加速落地于javacv实处
,需要做些什么工作呢
注:笔者这里只讨论javacv 调用ffmpeg
javacpp-platform
是javacv
为调用javacpp
所产生的文件ffmpeg-platform
相关jar包 是不同平台下编译ffmpeg后产生的库依赖文件,如linux系统是so文件;ffmpeg.jar
:提供对FFmpeg命令行工具的包装,使用户可以在Java应用程序中执行FFmpeg命令以读取、处理和写入音视频文件;是 javacpp编译出来调用ffmpeg的jni以往如果只是调用javacv,最省流的依赖导入方式
<dependency>
<groupId>org.bytedecogroupId>
<artifactId>javacv-platformartifactId>
<version>1.5.7version>
dependency>
实际你会发现很多依赖jar包是不需要用到的,那么精确于ffmpeg的依赖,有什么不需要导入多余依赖的方式呢
结合上述javacv认知的描述,依赖可以简化为以下图片所框选部分的依赖 + javacpp-你的操作系统:1.5.7 + ffmpeg.jar + ffmpeg-你的操作系统.jar包
那么回归正题,要在javacv 实现硬件加速需要注重的是哪些jar包呢:
答案:ffmpeg.jar ffmpeg-你的操作系统.jar包
FFmpegFrameGrabber frameGrabber = new FFmpegFrameGrabber(your video file);
frameGrabber.setVideoCodecName("h264_qsv");
frameGrabber.setOption("hwaccel","qsv");
frameGrabber.setOption("hwaccel_output_format","qsv");
笔者认为以上设置相当于ffmpeg 命令行形式
ffmpeg -hwaccel qsv -hwaccel_output_format qsv -c:v h264_qsv -i 10min39.mp4 -f null
基于前期调查,笔者一顿操作猛如虎,完成以下操作:
FFmpegFrameGrabber frameGrabber = new FFmpegFrameGrabber(dir);
frameGrabber.setVideoCodecName("h264_qsv");
frameGrabber.setOption("hwaccel","qsv");
frameGrabber.setOption("hwaccel_output_format","qsv");
frameGrabber.start();
Frame f = frameGrabber.grab();
while (frameGrabber.grab() != null){
}
frameGrabber.close();
运行结果如截图所示:现有ffmpeg依赖内置的VAAPI版本太低,无法使用系统安装的基于1.0 VAAPI版本的驱动程序,在Github Issue中也发现了与笔者遇到相同问题的人,ffmpeg官网作者说法是在 javacpp-presets下自行编译安装ffmpeg javacpp-presets
由此,笔者开始了漫长的自行编译之路,终于完成了ffmpeg的编译并mvn为jar包,成功实现javacv硬件解码。
可归纳为以下步骤:
apt-get install -y git vim
bzip2 xz-utils # 解压缩包使用
build-essential gcc cmake autoconf automake libtool pkg-config libdrm-dev # 编译使用
libmfx1 libmfx-tools libva-dev libmfx-dev vainfo intel-media-va-driver-non-free# vaapi + driver
export LIBVA_DRIVER_NAME=iHD
git clone https://github.com/Intel-Media-SDK/MediaSDK msdk
注:笔者基于ffmpeg5.0版本进行重新构建
git clone https://github.com/bytedeco/javacpp-presets.git
cd javacpp-presets
git checkout 1.5.7
vim ffmpeg/cppbuild.sh
#5.0版本的zlib是1.2.11 修改为1.2.13
ffmpeg 编译配置对头文件的路径增加一个 -I../include/mfx获取到libmfx库的头文件路径
# 删除mfx_dispatch的部分
注释掉mfx_dispatch 参照于ffmpeg_cppbuild.sh 否则会出现Error setting child device handle -17
# 具体还未找到原因
#清除原先编译的依赖
./cppbuild.sh -platform linux-x86_64 clean
#编译安装
./cppbuild.sh -platform linux-x86_64 install ffmpeg
# 笔者这里使用基于javacpp-presets目录下的相对路径
./ffmpeg/cppbuild/linux-x86_64/ffmpeg-5.0/ffmpeg -hwaccel qsv -hwaccel_output_format qsv -c:v h264_qsv -i 10min39.mp4 -f null - -benchmark
# 若出现对应错误:找不到某个so文件
sudo vim /etc/ld.so.conf
#添加安装库依赖路径到文件末尾
# INSTALL_PATH你的安装目录, 相对于javacpp-presets的位置是./ffmpeg/cppbuild/linux-x86_64/
$INSTALL_PATH/lib
#刷新
sudo /sbin/ldconfig -v
sudo ldconfig
// 命令行形式
String ffmpeg = Loader.load(org.bytedeco.ffmpeg.ffmpeg.class);
StopWatch stopWatch2 = new StopWatch();
ProcessBuilder pb = new ProcessBuilder(ffmpeg, "-hwaccel","qsv","-hwaccel_output_format", "qsv","-c:v", "h264_qsv", "-i", dir, "-f", "null", "-", "-benchmark");
stopWatch2.start();
pb.inheritIO().start().waitFor();
stopWatch2.stop();
System.out.println(" qsv command way =======" + stopWatch2.getTotalTimeSeconds());
// javacv API形式
FFmpegFrameGrabber frameGrabber = new FFmpegFrameGrabber(dir);
frameGrabber.setVideoCodecName("h264_qsv");
frameGrabber.setOption("hwaccel","qsv");
frameGrabber.setOption("hwaccel_output_format","qsv");
StopWatch stopWatch = new StopWatch();
stopWatch.start();
frameGrabber.start();
System.out.println("帧数:" + frameGrabber.getLengthInVideoFrames());
Frame f =frameGrabber.grab();
while (f != null){
f = frameGrabber.grab();
}
stopWatch.stop();
frameGrabber.stop();
System.out.println(" qsv =======" + stopWatch.getTotalTimeSeconds());
bgr24
,而ffmpeg本身默认的像素格式是yuv420p
frameGrabber.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
即可到此本文结束,欢迎大家纠错及分享ffmpeg重构建出现子设备句柄设置失败的原因。