2025-09-10
Spring Boot
0

目录

JavaCV视频压缩实战:从原理到实现
一、依赖配置优化
1.1 完整平台包(不推荐)
1.2 精简依赖方案(推荐),如果是其他操作系统引入对应系统环境的ffmpeg即可
1.3 Docker部署注意事项
二、视频压缩核心实现
三、关键技术解析
3.1 核心参数说明
3.2 FFmpeg关键配置
四、性能优化建议
五、异常处理增强
六、总结

JavaCV视频压缩实战:从原理到实现

一、依赖配置优化

在实际项目中,依赖管理是首要考虑的问题。JavaCV提供了两种依赖引入方式:

1.1 完整平台包(不推荐)

xml
<dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv-platform</artifactId> <version>1.5.12</version> <scope>provided</scope> </dependency>

问题:会导致JAR包增大650MB+,包含所有支持的库,多数情况下不必要。

1.2 精简依赖方案(推荐),如果是其他操作系统引入对应系统环境的ffmpeg即可

xml
<!-- 视频压缩工具 --> <dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv</artifactId> <version>1.5.12</version> </dependency> <!-- 支持linux系统环境ffmpeg --> <dependency> <groupId>org.bytedeco</groupId> <artifactId>ffmpeg</artifactId> <version>7.1.1-1.5.12</version> <classifier>linux-x86_64</classifier> </dependency> <!-- 支持windows系统环境ffmpeg --> <dependency> <groupId>org.bytedeco</groupId> <artifactId>ffmpeg</artifactId> <version>7.1.1-1.5.12</version> <classifier>windows-x86_64</classifier> </dependency>

1.3 Docker部署注意事项

必须使用eclipse-temurin:17基础镜像

dockerfile
FROM eclipse-temurin:17

注意

OpenJDK会导致运行时错误:Could not initialize class org.bytedeco.ffmpeg.global.avutil

二、视频压缩核心实现

以下是完整的视频压缩工具类实现,包含详细注释:

java
package com.truth.engine.common.util; import cn.hutool.core.io.FileUtil; import lombok.extern.slf4j.Slf4j; import org.bytedeco.ffmpeg.global.avcodec; import org.bytedeco.javacv.FFmpegFrameGrabber; import org.bytedeco.javacv.FFmpegFrameRecorder; import org.bytedeco.javacv.Frame; import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.IOException; @Slf4j public class VideoCompressionUtil { /** * 压缩视频文件 * * @param videoFile 上传的视频文件 * @param targetWidth 目标宽度(设置为0保持原始比例) * @param targetBitrate 目标比特率(设置为0自动计算) * @param quality 压缩质量(1-51, 越低质量越好) * @return 压缩后的视频字节数组 * @throws IOException 文件处理异常 */ public static byte[] compressVideo(MultipartFile videoFile, int targetWidth, int targetBitrate, int quality) throws IOException { // === 参数校验部分 === // 检查视频文件是否为空 if (videoFile == null || videoFile.isEmpty()) { throw new IllegalArgumentException("视频文件不能为空"); } // 检查质量参数是否在有效范围内(1-51) if (quality < 1 || quality > 51) { throw new IllegalArgumentException("质量参数必须在1-51之间"); } // === 临时文件创建 === // 创建临时输入文件(用于存储上传的原始视频) File tempInputFile = FileUtil.createTempFile("input_", ".mp4", true); // 创建临时输出文件(用于存储压缩后的视频) File tempOutFile = FileUtil.createTempFile("input_", ".mp4", true); try { // 将上传的视频文件转存到临时输入文件 videoFile.transferTo(tempInputFile); // === 视频抓取器初始化 === // 创建FFmpeg帧抓取器,用于读取视频信息 FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(tempInputFile); grabber.start(); // 启动视频抓取器 // === 计算目标视频参数 === // 获取原始视频宽度 int originalWidth = grabber.getImageWidth(); // 获取原始视频高度 int originalHeight = grabber.getImageHeight(); // 计算输出宽度(如果targetWidth为0则保持原始宽度) int outputWidth = targetWidth > 0 ? targetWidth : originalWidth; // 计算输出高度(保持原始宽高比) int outputHeight = targetWidth > 0 ? (int) (originalHeight * ((double) targetWidth / originalWidth)) : originalHeight; // === 计算比特率 === // 获取原始视频比特率 int originalBitrate = grabber.getVideoBitrate(); // 计算输出比特率(如果targetBitrate为0则使用原始比特率的70%) int outputBitrate = targetBitrate > 0 ? targetBitrate : (int) (originalBitrate * 0.7); // === 视频录制器初始化 === // 创建FFmpeg帧录制器,指定输出文件和分辨率 FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(tempOutFile, outputWidth, outputHeight); // === 设置视频编码参数 === recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); // 使用H.264编码 recorder.setFormat("mp4"); // 输出格式为MP4 recorder.setVideoBitrate(outputBitrate); // 设置视频比特率 recorder.setVideoQuality(quality); // 设置视频质量(1-51) // 设置FFmpeg高级选项: recorder.setVideoOption("preset", "fast"); // 编码速度预设(fast平衡速度和质量) recorder.setVideoOption("tune", "film"); // 优化场景(film针对电影/动画优化) recorder.setVideoOption("crf", String.valueOf(quality)); // 恒定质量因子 recorder.setFrameRate(grabber.getFrameRate()); // 保持原始帧率 // === 音频处理配置 === // 如果原始视频包含音频轨道 if (grabber.getAudioChannels() > 0) { recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC); // 使用AAC音频编码 recorder.setAudioChannels(grabber.getAudioChannels()); // 保持原始声道数 recorder.setAudioBitrate(grabber.getAudioBitrate()); // 保持原始音频比特率 recorder.setSampleRate(grabber.getSampleRate()); // 保持原始采样率 } // === 开始视频处理 === recorder.start(); // 启动视频录制器 // === 逐帧处理视频 === Frame frame; while (true) { // 抓取下一 { // 抓取下一帧(包含视频和音频帧) frame = grabber.grabFrame(); // 当没有更多帧时退出循环 if (frame == null) { break; } // 将当前帧写入输出视频 recorder.record(frame); } // === 资源释放 === // 停止并释放录制器资源(注意顺序:先停止录制器) recorder.stop(); // 停止录制 grabber.stop(); // 停止抓取 recorder.release(); // 释放录制器资源 grabber.release(); // 释放抓取器资源 recorder.close(); // 关闭录制器 grabber.close(); // 关闭抓取器 // 读取压缩后的视频文件到字节数组 return FileUtil.readBytes(tempOutFile); } finally { // === 清理临时文件 === // 记录临时文件路径(用于调试) log.info("tempInputFile:{}", tempInputFile.getAbsolutePath()); log.info("tempOutFile:{}", tempOutFile.getAbsolutePath()); // 删除临时输入文件 FileUtil.del(tempInputFile); // 删除临时输出文件 FileUtil.del(tempOutFile); } } /** * 默认参数的视频压缩 * * @param videoFile 上传的视频文件 * @return 压缩后的视频字节数组 * @throws Exception 处理异常 */ public static byte[] compressVideo(MultipartFile videoFile) throws Exception { // 使用默认参数调用压缩方法: // 目标宽度640px, 比特率自动计算(原始比特率的70%), 质量参数28(一般质量) return compressVideo(videoFile, 640, 0, 28); } }

三、关键技术解析

3.1 核心参数说明

参数说明推荐值
targetWidth目标宽度(像素)640/1280/1920
targetBitrate目标比特率(bps)0(自动计算)
quality压缩质量(1-51)23-28(平衡质量)

3.2 FFmpeg关键配置

  1. 编码预设(preset)
  • ultrafast:最快编码,最低压缩率
  • fast:平衡速度和质量(推荐)
  • slow:高质量,慢速编码
  1. 场景优化(tune)
  • film:电影/动画
  • stillimage:静态图像
  • fastdecode:快速解码
  1. 恒定质量因子(crf)
  • 18-23:高质量
  • 24-28:标准质量
  • 29+:低质量

四、性能优化建议

  1. 批量处理
java
// 使用线程池处理多个视频 ExecutorService executor = new ThreadPoolExecutor( Runtime.getRuntime().availableProcessors(), // 核心线程数 Runtime.getRuntime().availableProcessors(), // 最大线程数 0L, // 空闲线程存活时间 TimeUnit.MILLISECONDS, // 时间单位 new LinkedBlockingQueue<Runnable>() // 任务队列 );
  1. 内存优化
java
// 限制帧处理速率 recorder.setFrameRate(25); // 设置最大比特率 recorder.setVideoBitrate(1000000);
  1. 硬件加速(如果支持):
java
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264_QSV);

五、异常处理增强

java
try { grabber.start(); } catch (FFmpegFrameGrabber.Exception e) { log.error("视频处理失败 - 格式不支持或已损坏", e); throw new IllegalArgumentException("不支持的视频格式"); } try { recorder.start(); } catch (FFmpegFrameRecorder.Exception e) { log.error("视频编码器初始化失败", e); throw new IllegalStateException("视频编码失败"); }

六、总结

本文完整呈现了基于JavaCV的视频压缩解决方案,重点包括:

  1. 精简依赖配置方案
  2. 完整的视频压缩实现代码
  3. 关键参数说明和优化建议
  4. 异常处理和性能优化技巧

该方案已在生产环境验证,可直接集成到项目中,根据实际需求调整参数即可实现高效的视频压缩功能。