Javacv实现QSV硬件解码

Javacv实现QSV硬件解码

注:笔者服务器系统为Ubuntu 22.04 linux-x86_64,其他操作系统或许可参考于该博客,但笔者也没有完全试过

一、前景知识

在笔者之前的专栏博客中,已经完成ubuntu系统中编译安装ffmpeg集成qsv,实现硬件加速解码;实际应用过程中,使用javacv进行推流、解码、抽帧等,若要将硬件加速落地于javacv实处,需要做些什么工作呢

认识javacv

注:笔者这里只讨论javacv 调用ffmpeg

  • JavaCV是一个基于Java语言的开源计算机视觉库,它是通过调用底层的计算机视觉库实现图形图像处理和计算机视觉任务的。
  • JavaCV依赖于JavaCPP,JavaCPP是一个用于与本地C++代码交互的Java库,它通过生成JNI代码来实现Java与C++之间的相互调用。而FFmpeg是一组开源的音视频处理工具集,JavaCV使用了FFmpeg作为底层的视频处理库。
  • JavaCV的视频处理能力主要来自FFmpeg,而JavaCPP则提供了本地代码与Java代码之间的桥梁。
  • 通过上述描述 可以理解为一个调用的层级关系:javacv -> javacpp -> ffmpeg
  • 表现在pom文件上的jar包组成:
    • javacpp-platformjavacv为调用javacpp所产生的文件
    • ffmpeg-platform相关jar包 是不同平台下编译ffmpeg后产生的库依赖文件,如linux系统是so文件;
    • ffmpeg.jar:提供对FFmpeg命令行工具的包装,使用户可以在Java应用程序中执行FFmpeg命令以读取、处理和写入音视频文件;是 javacpp编译出来调用ffmpeg的jni
javacv_ffmpeg maven依赖

以往如果只是调用javacv,最省流的依赖导入方式

        <!-- javacv 多平台 -->
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacv-platform</artifactId>
            <version>1.5.7</version>
        </dependency>

实际你会发现很多依赖jar包是不需要用到的,那么精确于ffmpeg的依赖,有什么不需要导入多余依赖的方式呢
结合上述javacv认知的描述,依赖可以简化为以下图片所框选部分的依赖 + javacpp-你的操作系统:1.5.7 + ffmpeg.jar + ffmpeg-你的操作系统.jar包

在这里插入图片描述

那么回归正题,要在javacv 实现硬件加速需要注重的是哪些jar包呢:

答案:ffmpeg.jar ffmpeg-你的操作系统.jar包

二、前期关于javacv 实现硬件加速的调查

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

三、Javacv实战

基于前期调查,笔者一顿操作猛如虎,完成以下操作:

  • 安装 vaapi、intel media driver 等相关依赖
  • 直接引入javacv:5.0-1.5.7 现有依赖,简单的编写以下代码,打包了一个demo jar包,在服务器上运行
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硬件解码。

可归纳为以下步骤:

  • 下载libva、git 、intel 驱动程序等相关依赖
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

  • 下载intel media sdk
git clone https://github.com/Intel-Media-SDK/MediaSDK msdk
  • javacpp-preset下重新编译ffmpeg
    注:笔者基于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
  • 对安装目录下的ffmpeg进行qsv命令执行
# 笔者这里使用基于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
  • 如果编译安装ffmpeg之后,使用ffmpeg命令行形式能够实现硬件加速,那么就可以直接进行mvn 打包
  • 替换ffmpeg-version .jar & ffmpeg- platform-version.jar包依赖

四、Javacv 硬件加速效果

// 命令行形式
 		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());
  • 运行结果:
    发现javacv API 方式耗时竟然是命令行形式的三倍,经过debug发现javacv调用ffmpeg所设置的默认像素格式是bgr24,而ffmpeg本身默认的像素格式是yuv420p
    设置frameGrabber.setPixelFormat(avutil.AV_PIX_FMT_YUV420P); 即可
    当然具体像素格式还取决于具体你所应用的场景

到此本文结束,欢迎大家纠错及分享ffmpeg重构建出现子设备句柄设置失败的原因。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值