feat:更新文档架构
This commit is contained in:
243
docs/Web/Others/VideoCompressor.md
Normal file
243
docs/Web/Others/VideoCompressor.md
Normal file
@@ -0,0 +1,243 @@
|
||||
---
|
||||
title: 视频压缩
|
||||
date: 2026-01-29
|
||||
---
|
||||
|
||||
# 一、简介
|
||||
  视频码率(Bit Rate),指的是视频文件在单位时间内(通常是每秒)所包含的数据量。单位通常是 Mbps(兆比特每秒)或 Kbps(千比特每秒)。
|
||||
  码率越高:视频中包含的细节信息越多,画面越清晰、色彩越丰富、动态画面(如快速运动、爆炸等)越流畅,不容易出现马赛克或模糊。但代价是文件体积越大,传输所需的网络带宽也越高。码率越低:视频文件更小,传输更快,但在复杂画面中容易产生压缩瑕疵(如马赛克、模糊、色带等)。
|
||||
  如果不压缩,原始视频数据量极其庞大,一部2小时的未压缩1080p电影可能占用数TB的存储空间,普通硬盘无法承受,家庭宽带或移动网络无法实时传输如此巨大的数据量,视频播放会不断卡顿。
|
||||
  如果服务器的带宽为10M,适合 720p,勉强支持低码率 1080p。
|
||||
|
||||
# 二、引入依赖
|
||||
```xml
|
||||
<!-- Jave 2 视频处理库 -->
|
||||
<dependency>
|
||||
<groupId>ws.schild</groupId>
|
||||
<artifactId>jave-all-deps</artifactId>
|
||||
<version>3.5.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
# 三、工具类
|
||||
```java
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import ws.schild.jave.Encoder;
|
||||
import ws.schild.jave.MultimediaObject;
|
||||
import ws.schild.jave.encode.AudioAttributes;
|
||||
import ws.schild.jave.encode.EncodingAttributes;
|
||||
import ws.schild.jave.encode.VideoAttributes;
|
||||
import ws.schild.jave.info.VideoSize;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* 视频压缩工具类
|
||||
* 提供将视频压缩到720p的静态方法
|
||||
*/
|
||||
@Slf4j
|
||||
public class VideoCompressor {
|
||||
|
||||
// 支持的视频格式
|
||||
private static final String[] SUPPORTED_FORMATS = {
|
||||
".mp4", ".avi", ".mov", ".mkv", ".flv",
|
||||
".wmv", ".webm", ".mpeg", ".mpg", ".3gp"
|
||||
};
|
||||
|
||||
// 压缩参数常量
|
||||
private static final int TARGET_WIDTH = 1280;
|
||||
private static final int TARGET_HEIGHT = 720;
|
||||
private static final int VIDEO_BITRATE = 1500000; // 1.5 Mbps
|
||||
private static final int AUDIO_BITRATE = 128000; // 128 kbps
|
||||
private static final String VIDEO_CODEC = "libx264";
|
||||
private static final String AUDIO_CODEC = "aac";
|
||||
private static final String OUTPUT_FORMAT = "mp4";
|
||||
private static final String OUTPUT_SUFFIX = "_720p.mp4";
|
||||
|
||||
private static final int MAX_BITRATE = 2500; // 2.5 Mbps
|
||||
private static final int MIN_BITRATE = 1000; // 1 Mbps
|
||||
|
||||
/**
|
||||
* 压缩视频到720p,统一输出为MP4格式
|
||||
*
|
||||
* @param inputFile 输入视频文件
|
||||
* @return 压缩后的视频文件,如果不需要压缩则返回原文件
|
||||
* @throws Exception 压缩过程中可能出现的异常
|
||||
*/
|
||||
public static File compressTo720p(File inputFile) throws Exception {
|
||||
if (!isSupportedVideoFile(inputFile)) {
|
||||
log.info("文件格式不支持或文件不存在,直接返回源文件");
|
||||
return inputFile;
|
||||
}
|
||||
|
||||
// 判断是否需要压缩
|
||||
if (!needCompression(inputFile)) {
|
||||
return inputFile;
|
||||
}
|
||||
|
||||
File outputFile = createOutputFile(inputFile);
|
||||
Encoder encoder = new Encoder();
|
||||
|
||||
// 配置编码参数
|
||||
EncodingAttributes encodingAttributes = createEncodingAttributes();
|
||||
|
||||
log.info("开始压缩视频: {} -> {}", inputFile.getName(), outputFile.getName());
|
||||
encoder.encode(new MultimediaObject(inputFile), outputFile, encodingAttributes);
|
||||
log.info("视频压缩完成: {}", outputFile.getName());
|
||||
|
||||
return outputFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* 智能判断视频是否需要压缩
|
||||
* 基于分辨率、码率、数据密度等多维度综合判断
|
||||
*/
|
||||
public static boolean needCompression(File videoFile) throws Exception {
|
||||
MultimediaObject media = new MultimediaObject(videoFile);
|
||||
|
||||
// 获取视频基本信息
|
||||
double fileSizeMB = videoFile.length() / 1048576.0; // 转换为MB
|
||||
double duration = media.getInfo().getDuration() / 1000.0;
|
||||
int bitRateKbps = media.getInfo().getVideo().getBitRate() / 1000; // 转换为kbps
|
||||
|
||||
// 获取视频分辨率
|
||||
int width = media.getInfo().getVideo().getSize().getWidth();
|
||||
int height = media.getInfo().getVideo().getSize().getHeight();
|
||||
|
||||
log.info("视频分析: 分辨率 {}x{}, 大小 {}MB, 时长 {}秒, 码率约 {}kbps", width, height, fileSizeMB, duration, bitRateKbps);
|
||||
|
||||
// 判断逻辑:基于分辨率和码率的智能判断
|
||||
boolean needsCompression = false;
|
||||
|
||||
// 1. 如果分辨率已经<=720p,检查码率是否过高
|
||||
if (height <= TARGET_HEIGHT && width <= TARGET_WIDTH) {
|
||||
if (bitRateKbps > MAX_BITRATE) {
|
||||
log.info("视频分辨率达标但码率({}kbps)过高,需要压缩", bitRateKbps);
|
||||
needsCompression = true;
|
||||
} else {
|
||||
log.info("视频分辨率已达720p或以下,且码率合理,无需压缩");
|
||||
}
|
||||
}
|
||||
// 2. 如果分辨率>720p,但码率很低,说明已是优化过的视频,无需压缩
|
||||
else if (height > TARGET_HEIGHT && bitRateKbps < MIN_BITRATE) {
|
||||
log.info("视频虽为{}p高分辨率,但码率({}kbps)已优化,无需压缩", height, bitRateKbps);
|
||||
}
|
||||
// 3. 高分辨率+高码率,需要压缩
|
||||
else if (height > TARGET_HEIGHT && bitRateKbps >= MAX_BITRATE) {
|
||||
log.info("视频为{}p高分辨率且码率较高({}kbps),需要压缩至720p", height, bitRateKbps);
|
||||
needsCompression = true;
|
||||
}
|
||||
// 4. 其他情况(如异常数据)默认不压缩
|
||||
else {
|
||||
log.info("视频参数异常,保守处理:不进行压缩");
|
||||
}
|
||||
|
||||
return needsCompression;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查文件是否为支持的视频格式
|
||||
*/
|
||||
private static boolean isSupportedVideoFile(File file) {
|
||||
if (file == null || !file.exists()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String filename = file.getName().toLowerCase();
|
||||
for (String format : SUPPORTED_FORMATS) {
|
||||
if (filename.endsWith(format)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建输出文件对象
|
||||
*/
|
||||
private static File createOutputFile(File inputFile) {
|
||||
String baseName = getFileBaseName(inputFile.getName());
|
||||
String outputFilename = baseName + OUTPUT_SUFFIX;
|
||||
return new File(inputFile.getParent(), outputFilename);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件名(不含扩展名)
|
||||
*/
|
||||
private static String getFileBaseName(String filename) {
|
||||
int dotIndex = filename.lastIndexOf('.');
|
||||
return (dotIndex == -1) ? filename : filename.substring(0, dotIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建编码配置参数
|
||||
*/
|
||||
private static EncodingAttributes createEncodingAttributes() {
|
||||
// 音频配置
|
||||
AudioAttributes audio = new AudioAttributes();
|
||||
audio.setCodec(AUDIO_CODEC);
|
||||
audio.setBitRate(AUDIO_BITRATE);
|
||||
|
||||
// 视频配置
|
||||
VideoAttributes video = new VideoAttributes();
|
||||
video.setCodec(VIDEO_CODEC);
|
||||
video.setBitRate(VIDEO_BITRATE);
|
||||
video.setSize(new VideoSize(TARGET_WIDTH, TARGET_HEIGHT));
|
||||
|
||||
// 编码设置
|
||||
EncodingAttributes attributes = new EncodingAttributes();
|
||||
attributes.setOutputFormat(OUTPUT_FORMAT);
|
||||
attributes.setAudioAttributes(audio);
|
||||
attributes.setVideoAttributes(video);
|
||||
|
||||
return attributes;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
  通过分辨率和码率的双重判断,确定是否需要压缩。
|
||||
|
||||
# 四、使用
|
||||
```java
|
||||
public void uploadFile(MultipartFile file, String filePath) {
|
||||
try {
|
||||
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
||||
|
||||
File tempFile = convertMultipartFileToFile(file);
|
||||
log.info("临时文件地址:{}", tempFile.toPath());
|
||||
|
||||
log.info("开始压缩文件:{}", tempFile.getName());
|
||||
File compressFile = VideoCompressor.compressTo720p(tempFile);
|
||||
log.info("完成压缩文件:{}", tempFile.getName());
|
||||
|
||||
log.info("压缩文件地址:{}", tempFile.toPath());
|
||||
|
||||
body.add("file", new FileSystemResource(compressFile));
|
||||
|
||||
aListClient.put()
|
||||
.uri("api/fs/form")
|
||||
.header(HttpHeaders.AUTHORIZATION, token)
|
||||
.header("File-Path", filePath)
|
||||
.contentType(MediaType.MULTIPART_FORM_DATA)
|
||||
.body(body)
|
||||
.retrieve()
|
||||
.body(Map.class);
|
||||
|
||||
Files.deleteIfExists(compressFile.toPath());
|
||||
Files.deleteIfExists(tempFile.toPath());
|
||||
} catch (Exception e) {
|
||||
log.error("文件上传失败{}", e.getMessage());
|
||||
throw new RuntimeException("文件上传失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
private File convertMultipartFileToFile(MultipartFile file) throws IOException {
|
||||
String suffix = (file.getOriginalFilename() != null && file.getOriginalFilename().contains("."))
|
||||
? file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."))
|
||||
: ".tmp";
|
||||
|
||||
Path tempFile = Files.createTempFile(System.currentTimeMillis() + "", suffix);
|
||||
file.transferTo(tempFile);
|
||||
return tempFile.toFile();
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user