上传视频,根据视频解析第1帧,获取图像,网上错的比较多;
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.9</version>
</dependency>
<!-- Additional dependencies required to use CUDA and cuDNN -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv-platform-gpu</artifactId>
<version>4.7.0-1.5.9</version>
</dependency>
<!-- Optional GPL builds with (almost) everything enabled -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg-platform-gpl</artifactId>
<version>6.0-1.5.9</version>
</dependency>
先加上pom依赖, 以下是工具类
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.File;
import java.math.BigDecimal;
import java.nio.Buffer;
import java.nio.IntBuffer;
import java.util.Map;
/**
* https://www.jianshu.com/p/50f6a914041f
* https://blog.csdn.net/wangzhezhilu001/article/details/131655683
* 获取视频信息
* @author
*/
@Slf4j
public class VideoInfoUtils {
public static void main(String[] args)throws Exception {
String videoFilePath ="/XXX/2024-08-26.mov";
// videoFilePath = "/XX/3e0f7c5de73d6c9a0.mp4";
File videoFile =new File(videoFilePath);
VideoInfo videoInfo =getVideoInfo(videoFile);
log.info("videoInfo:{}", videoInfo);
String targetImgPath ="/XX/cover3.jpg";
File targetFile =new File(targetImgPath);
boolean tempPath =generateFirstFrameFace(targetFile, videoFile, videoInfo.getRotation());
}
public static VideoInfogetVideoInfo(File file) {
VideoInfo videoInfo =new VideoInfo();
FFmpegFrameGrabber grabber =null;
try {
grabber =new FFmpegFrameGrabber(file);
// 启动 FFmpeg
grabber.start();
Map videoSideData = grabber.getVideoSideData();
// 横拍的视频, {Display Matrix=java.nio.DirectByteBuffer[pos=0 lim=36 cap=36]}
log.info("VideoSideData:{}", videoSideData);
Buffer displayMatrixBuffer = videoSideData.get("Display Matrix");
if (displayMatrixBuffer !=null) {
java.nio.MappedByteBuffer directByteBuffer = (java.nio.MappedByteBuffer) displayMatrixBuffer;
IntBuffer displayMatrixIntBuffer = directByteBuffer.asIntBuffer();
// 创建 int[] 数组并复制数据
int[] displayMatrix =new int[displayMatrixIntBuffer.limit()];
displayMatrixIntBuffer.get(displayMatrix);
float rotation =calculateRotation(displayMatrix);
log.info("rotation:{}", rotation);
videoInfo.setRotation(rotation);
}
// 读取视频帧数
videoInfo.setLengthInFrames(grabber.getLengthInVideoFrames());
// 读取视频帧率
videoInfo.setFrameRate(grabber.getVideoFrameRate());
// 读取视频秒数
videoInfo.setDuration(new BigDecimal(grabber.getLengthInTime()).divide(new BigDecimal("1000000")).intValue());
// 读取视频宽度
videoInfo.setWidth(grabber.getImageWidth());
// 读取视频高度
videoInfo.setHeight(grabber.getImageHeight());
videoInfo.setAudioChannel(grabber.getAudioChannels());
videoInfo.setVideoCode(grabber.getVideoCodecName());
videoInfo.setAudioCode(grabber.getAudioCodecName());
// String md5 = MD5Util.getMD5ByInputStream(new FileInputStream(file));
videoInfo.setSampleRate(grabber.getSampleRate());
videoInfo.setFormat(grabber.getFormat());
return videoInfo;
}catch (Exception e) {
e.printStackTrace();
return null;
}finally {
try {
if (grabber !=null) {
// 此处代码非常重要,如果没有,可能造成 FFmpeg 无法关闭
grabber.stop();
grabber.release();
}
}catch (FFmpegFrameGrabber.Exception e) {
log.error("getVideoInfo grabber.release failed 获取文件信息失败:{}", e.getMessage());
}
}
}
/**
* 计算旋转角度
* @param displayMatrix
* @return
*/
private static float calculateRotation(int[] displayMatrix) {
if (displayMatrix.length !=9) {
throw new IllegalArgumentException("Display Matrix should have 9 elements.");
}
// 解析旋转角度
// a = displayMatrix[0], b = displayMatrix[1], d = displayMatrix[3], e = displayMatrix[4]
float a = displayMatrix[0], b = displayMatrix[1], d = displayMatrix[3], e = displayMatrix[4];
double radians = Math.atan2(b, a); // 逆时针角度
double degrees = Math.toDegrees(radians);
// 确保角度范围在 [0, 360) 之间
degrees = degrees <0 ?360 + degrees : degrees;
return (float) degrees;
}
/**
* 生成视频的首帧图片
* @throws
* @Title: getTempPath
* @param: @param tempPath 生成首帧图片的文件地址
* @param: @param filePath 传进来的线上文件
* @param: @return
* @param: @throws Exception
* @return: boolean
*/
public static boolean generateFirstFrameFace(File targetFile, File videoFile, float rotation)throws Exception {
FFmpegFrameGrabber ff =new FFmpegFrameGrabber(videoFile);
ff.start();
int ftp = ff.getLengthInFrames();
int flag =0;
Frame frame =null;
while (flag <= ftp) {
//获取帧
frame = ff.grabImage();
//过滤前3帧,避免出现全黑图片
if ((flag >3) && (frame !=null)) {
break;
}
flag++;
}
if (ImageIO.write(FrameToBufferedImage(frame, rotation), "jpg", targetFile)) {
ff.close();
ff.stop();
log.info("输出图片成功!");
return true;
}else {
ff.close();
ff.stop();
log.info("输出图片失败!");
return false;
}
}
/***
*
* @Title: FrameToBufferedImage
* @Description: 创建格式化BufferedImage对象
* @param: @param frame
* @param: @return
* @return: RenderedImage
* @throws
*/
private static RenderedImageFrameToBufferedImage(Frame frame, float rotation) {
//创建BufferedImage对象
Java2DFrameConverter converter =new Java2DFrameConverter();
BufferedImage bufferedImage = converter.getBufferedImage(frame);
if (rotation >0) {
log.info("旋转图片");
bufferedImage =rotateImage(bufferedImage, rotation);
}
return bufferedImage;
}
public static BufferedImagerotateImage(BufferedImage originalImage, double angle) {
// 计算旋转角度的弧度
double radians = Math.toRadians(angle);
// 获取原始图像的宽度和高度
int originalWidth = originalImage.getWidth();
int originalHeight = originalImage.getHeight();
// 计算旋转后图像的宽度和高度
int rotatedWidth = (int) Math.abs(originalWidth * Math.cos(radians)) + (int) Math.abs(originalHeight * Math.sin(radians));
int rotatedHeight = (int) Math.abs(originalHeight * Math.cos(radians)) + (int) Math.abs(originalWidth * Math.sin(radians));
// 创建一个新的图像对象,大小为旋转后的宽度和高度
BufferedImage rotatedImage =new BufferedImage(rotatedWidth, rotatedHeight, originalImage.getType());
// 获取旋转变换对象
AffineTransform transform =new AffineTransform();
transform.translate((rotatedWidth - originalWidth) /2, (rotatedHeight - originalHeight) /2);
transform.rotate(radians, originalWidth /2.0, originalHeight /2.0);
// 使用 Graphics2D 对象进行图像旋转
Graphics2D g2d = rotatedImage.createGraphics();
g2d.setTransform(transform);
g2d.drawImage(originalImage, 0, 0, null);
g2d.dispose();
return rotatedImage;
}
}
再者是返回的结果对象:
@Getter
@Setter
@ToString
public class VideoInfo {
/**
* 总帧数
**/
private int lengthInFrames;
/**
* 帧率
**/
private double frameRate;
/**
* 时长
**/
private int duration;
/**
* 视频编码
*/
private StringvideoCode;
/**
* 音频编码
*/
private StringaudioCode;
private int width;
private int height;
private int audioChannel;
private Stringmd5;
/**
* 音频采样率
*/
private IntegersampleRate;
/**
* 视频格式
*/
private Stringformat;
/**
* 视频旋转角度
*/
private float rotation;
}