From ffc398d416aa4ba4f294c0cb86964e5cecff2ee7 Mon Sep 17 00:00:00 2001
From: liusuyi <1951119284@qq.com>
Date: Wed, 19 Nov 2025 11:37:33 +0800
Subject: [PATCH] 增加媒体库视频缩略图
---
ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskServiceServiceImpl.java | 176 ++++++++++++++++++++++++++++++++++++++++++++++++----------
1 files changed, 144 insertions(+), 32 deletions(-)
diff --git a/ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskServiceServiceImpl.java b/ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskServiceServiceImpl.java
index 38f4c77..5533884 100644
--- a/ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskServiceServiceImpl.java
+++ b/ard-work/src/main/java/com/ard/work/service/impl/ArdFlightTaskServiceServiceImpl.java
@@ -17,20 +17,32 @@
import com.ard.work.service.IArdFlightTaskService;
import com.ard.work.service.IArdServerInfoService;
import lombok.extern.slf4j.Slf4j;
+import org.bytedeco.javacpp.BytePointer;
+import org.bytedeco.javacv.FFmpegFrameGrabber;
+import org.bytedeco.javacv.Frame;
+import org.bytedeco.javacv.Java2DFrameConverter;
+import org.bytedeco.opencv.global.opencv_imgproc;
+import org.bytedeco.opencv.opencv_core.Size;
+import org.opencv.core.Mat;
+import org.opencv.videoio.VideoCapture;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
+import javax.imageio.ImageIO;
import javax.net.ssl.*;
-import java.io.InputStream;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferByte;
+import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
-import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
@Slf4j
@Service
@@ -62,7 +74,6 @@
/**
* 同步所有服务的飞行历史
- *
*/
@Scheduled(fixedDelay = 10000)
public void syncAllFlightHistory() {
@@ -162,21 +173,42 @@
private void insertVideoToDatabase(ArdFlightTask flightTask, List<String> videoPathList) {
if (videoPathList != null && !videoPathList.isEmpty()) {
for (String videoPath : videoPathList) {
- ArdMedia ardMedia = ArdMedia.builder()
+ ArdMedia ardMedia = ArdMedia.builder()
.id(IdUtils.simpleUUID())
.taskId(flightTask.getTaskId())
.mediaType(1) // 1表示视频
.sourceUrl(videoPath)
.fileSize(getFileSizeByUrl(videoPath))
.build();
- // 异步上传文件到OSS,并在上传完成后插入数据库
- CompletableFuture.supplyAsync(() -> uploadFileToOss(flightTask.getBoxSn(), videoPath, "record1",
- MimeTypeUtils.VIDEO_MP4), threadPoolTaskExecutor)
- .thenAccept(url -> {
- ardMedia.setMediaUrl(url); // 获取上传后的文件 URL
- ardMedia.setCreateTime(new Date());
- ardMediaMapper.insertArdMedia(ardMedia); // 插入数据库
- });
+ CompletableFuture.supplyAsync(() -> {
+ try {
+ // 上传视频
+ String videoUrl = uploadFileToOss(flightTask.getBoxSn(), videoPath, "record",
+ MimeTypeUtils.VIDEO_MP4);
+ ardMedia.setMediaUrl(videoUrl);
+ // 生成缩略图 InputStream
+ ByteArrayInputStream thumbnailStream = generateThumbnailInputStream(videoPath, 2, 200);
+ if (thumbnailStream != null) {
+ String thumbnailUrl = ossServiceContext.upload("record",
+ flightTask.getBoxSn() + "/" + IdUtils.fastSimpleUUID() + ".jpg",
+ thumbnailStream,
+ MimeTypeUtils.IMAGE_JPEG);
+ ardMedia.setThumbnailUrl(thumbnailUrl);
+ }
+
+ ardMedia.setCreateTime(new Date());
+ return ardMedia;
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }, threadPoolTaskExecutor).thenAccept(am -> {
+ if (am != null) {
+ ardMediaMapper.insertArdMedia(am);
+ }
+ });
+
}
}
}
@@ -203,30 +235,50 @@
if (!newVideos.isEmpty()) {
// 插入新的视频记录
for (String videoPath : newVideos) {
- ArdMedia ardMedia = ArdMedia.builder()
+ ArdMedia ardMedia = ArdMedia.builder()
.id(IdUtils.simpleUUID())
.taskId(flightTask.getTaskId())
.mediaType(1) // 1表示视频
.fileSize(getFileSizeByUrl(videoPath))
.sourceUrl(videoPath)
.build();
- // 异步上传文件到OSS,并在上传完成后插入数据库
- CompletableFuture.supplyAsync(() -> uploadFileToOss(flightTask.getBoxSn(), videoPath, "record1",
- MimeTypeUtils.VIDEO_MP4), threadPoolTaskExecutor)
- .thenAccept(url -> {
- ardMedia.setMediaUrl(url); // 获取上传后的文件 URL
- ardMedia.setCreateTime(new Date());
- ardMediaMapper.insertArdMedia(ardMedia); // 插入数据库
- });
- }
+ CompletableFuture.supplyAsync(() -> {
+ try {
+ // 上传视频
+ String videoUrl = uploadFileToOss(flightTask.getBoxSn(), videoPath, "record",
+ MimeTypeUtils.VIDEO_MP4);
+ ardMedia.setMediaUrl(videoUrl);
+
+ // 生成缩略图 InputStream
+ ByteArrayInputStream thumbnailStream = generateThumbnailInputStream(videoPath, 2, 200);
+ if (thumbnailStream != null) {
+ String thumbnailUrl = ossServiceContext.upload("record",
+ flightTask.getBoxSn() + "/" + IdUtils.fastSimpleUUID() + ".jpg",
+ thumbnailStream,
+ MimeTypeUtils.IMAGE_JPEG);
+ ardMedia.setThumbnailUrl(thumbnailUrl);
+ }
+
+ ardMedia.setCreateTime(new Date());
+ return ardMedia;
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }, threadPoolTaskExecutor).thenAccept(am -> {
+ if (am != null) {
+ ardMediaMapper.insertArdMedia(am);
+ }
+ });
+ }
// 更新任务的视频数量
flightTask.setVideoCount(flightTask.getVideoCount() + newVideos.size());
ardFlightTaskMapper.updateArdFlightTask(flightTask);
}
}
}
-
// 插入图片记录
private void insertPhotoToDatabase(ArdFlightTask flightTask, String serverId, String boxSn) {
@@ -245,7 +297,7 @@
private void updatePhotoToDatabase(ArdFlightTask flightTask, String serverId, String boxSn) {
int count = flightTask.getPhotoCount(); //获取本地库图片数量
// 查询图片
- BoxPhotosDTO boxPhotosDTO = BoxPhotosDTO.builder()
+ BoxPhotosDTO boxPhotosDTO = BoxPhotosDTO.builder()
.taskId(flightTask.getTaskId())
.boxSn(boxSn)
.pageIndex(1)
@@ -296,7 +348,7 @@
.captureTime(DateUtils.dateTime(DateUtils.YYYY_MM_DD_HH_MM_SS, photo.getTimeStamp()))
.build();
// 异步上传文件到OSS,并在上传完成后插入数据库
- CompletableFuture.supplyAsync(() -> uploadFileToOss(flightTask.getBoxSn(), photo.getImgUrl(), "pic1",
+ CompletableFuture.supplyAsync(() -> uploadFileToOss(flightTask.getBoxSn(), photo.getImgUrl(), "pic",
MimeTypeUtils.IMAGE_JPG), threadPoolTaskExecutor)
.thenAccept(url -> {
ardMedia.setMediaUrl(url); // 获取上传后的文件 URL
@@ -337,12 +389,7 @@
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// 创建所有主机名都信任的HostnameVerifier
- HostnameVerifier allHostsValid = new HostnameVerifier() {
- @Override
- public boolean verify(String hostname, SSLSession session) {
- return true;
- }
- };
+ HostnameVerifier allHostsValid = (hostname, session) -> true;
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
// 现在可以正常访问
@@ -374,7 +421,7 @@
return fileOssUrl; // 返回上传后的文件OSS URL
}
- //获取文件大小
+ // 获取文件大小
private long getFileSizeByUrl(String fileUrl) {
try {
URL url = new URL(fileUrl);
@@ -387,4 +434,69 @@
}
}
+ // 生成视频缩略图
+ public ByteArrayInputStream generateThumbnailInputStream(String videoFile,
+ int skipFrames,
+ int targetWidth) throws Exception {
+ long start = System.currentTimeMillis();
+ FFmpegFrameGrabber grabber = null;
+
+ try {
+ grabber = new FFmpegFrameGrabber(videoFile);
+ grabber.start();
+
+ int totalFrames = grabber.getLengthInFrames();
+ System.out.println("视频总帧数: " + totalFrames);
+ System.out.println("视频时长: " + grabber.getLengthInTime() / 1000000 + "秒");
+
+ Frame frame = null;
+ int currentFrame = 0;
+
+ // 读取指定帧数后的第一帧有效图像
+ while (currentFrame < totalFrames) {
+ frame = grabber.grabImage();
+ if (frame == null) {
+ break;
+ }
+
+ if (currentFrame > skipFrames && frame.image != null) {
+ break;
+ }
+ currentFrame++;
+ }
+
+ if (frame == null || frame.image == null) {
+ throw new Exception("无法获取有效视频帧");
+ }
+
+ // 转换 Frame 为 BufferedImage
+ Java2DFrameConverter converter = new Java2DFrameConverter();
+ BufferedImage image = converter.getBufferedImage(frame);
+
+ // 按目标宽度等比缩放
+ int targetHeight = (int) ((double) targetWidth / image.getWidth() * image.getHeight());
+ BufferedImage scaledImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_3BYTE_BGR);
+ scaledImage.getGraphics().drawImage(image, 0, 0, targetWidth, targetHeight, null);
+
+ // 转成 ByteArrayInputStream
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ImageIO.write(scaledImage, "jpg", baos);
+ long cost = System.currentTimeMillis() - start;
+ System.out.println("处理耗时: " + cost + "ms");
+
+ return new ByteArrayInputStream(baos.toByteArray());
+
+ } finally {
+ if (grabber != null) {
+ try {
+ grabber.stop();
+ grabber.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+
}
--
Gitblit v1.9.3