Files
TimiServerAPI/src/main/java/com/imyeyu/api/util/JavaCV.java
T
2026-08-21 23:43:40 +08:00

65 lines
2.0 KiB
Java

package com.imyeyu.api.util;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
/// JavaCV 工具类
///
/// @author 夜雨
/// @since 2025-10-23 17:05
public class JavaCV {
/// 从视频流顺序解码并提取指定时间附近的一帧 PNG 图片
///
/// 不执行时间定位,避免为了取实时缩略图而把整个视频复制到临时文件
///
/// @param stream 视频输入流
/// @param targetSeconds 目标时间,单位为秒
/// @return PNG 图片字节流
/// @throws Exception 视频解析或取帧失败
public static ByteArrayOutputStream captureThumbnail(InputStream stream, double targetSeconds) throws Exception {
long targetMicros = toMicros(targetSeconds);
ByteArrayOutputStream firstFrame = new ByteArrayOutputStream();
try (FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(stream)) {
grabber.start();
try (Java2DFrameConverter converter = new Java2DFrameConverter()) {
Frame frame;
while ((frame = grabber.grabImage()) != null) {
BufferedImage image = converter.getBufferedImage(frame);
if (image == null) {
continue;
}
if (!(0 < firstFrame.size())) {
firstFrame = toPng(image);
}
if (grabber.getTimestamp() >= targetMicros) {
return targetMicros == 0 ? firstFrame : toPng(image);
}
}
}
}
// 短视频提前结束时退回首个有效帧
return firstFrame;
}
private static long toMicros(double targetSeconds) {
double seconds = Double.isFinite(targetSeconds) ? Math.max(0, targetSeconds) : 0;
return Math.round(seconds * 1_000_000D);
}
private static ByteArrayOutputStream toPng(BufferedImage image) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
if (ImageIO.write(image, "png", output)) {
return output;
}
return new ByteArrayOutputStream();
}
}