Java-视频按秒截取帧

秒速五厘米 2023-10-01 22:29 96阅读 0赞

Java视频按秒截取帧

1. Java视频按秒截取帧工具类

  1. 具体方法都在工具类中,可以文件地址,可以按照参数传递,
  2. 基本逻辑就是获取文件下载地址,将文件类型进行转换,然后传入javacv进行读取流操作。
  3. 具体场景需要根据工具类具体改造一下,但是基本原理是不变的。
  4. <!--视频流-->
  5. <!-- https://mvnrepository.com/artifact/com.github.vip-zpf/jave -->
  6. <dependency>
  7. <groupId>com.github.vip-zpf</groupId>
  8. <artifactId>jave</artifactId>
  9. <version>1.1.1</version>
  10. </dependency>
  11. <dependency>
  12. <groupId>com.github.vip-zpf</groupId>
  13. <artifactId>jave</artifactId>
  14. <version>1.0.8</version>
  15. </dependency>
  16. <dependency>
  17. <groupId>org.bytedeco</groupId>
  18. <artifactId>javacv</artifactId>
  19. <version>1.5.6</version>
  20. </dependency>
  21. <dependency>
  22. <groupId>org.bytedeco</groupId>
  23. <artifactId>ffmpeg-platform</artifactId>
  24. <version>4.4-1.5.6</version>
  25. </dependency>
  26. package com.wondertek.util;
  27. import com.wondertek.util.img.ConvertToMultipartFile;
  28. import com.wondertek.web.fastdfs.DRFdfsUpAndDowService;
  29. import it.sauronsoftware.jave.Encoder;
  30. import it.sauronsoftware.jave.EncoderException;
  31. import it.sauronsoftware.jave.MultimediaInfo;
  32. import lombok.extern.slf4j.Slf4j;
  33. import org.bytedeco.javacv.FFmpegFrameGrabber;
  34. import org.bytedeco.javacv.Frame;
  35. import org.bytedeco.javacv.Java2DFrameConverter;
  36. import org.springframework.beans.factory.annotation.Autowired;
  37. import org.springframework.stereotype.Component;
  38. import org.springframework.web.multipart.MultipartFile;
  39. import javax.annotation.PostConstruct;
  40. import javax.imageio.ImageIO;
  41. import java.awt.*;
  42. import java.awt.image.BufferedImage;
  43. import java.io.*;
  44. import java.net.URLDecoder;
  45. import java.util.ArrayList;
  46. import java.util.HashMap;
  47. import java.util.List;
  48. import java.util.Map;
  49. /**
  50. * @author xxx
  51. */
  52. @Component
  53. @Slf4j
  54. public class VideoUtil {
  55. private static DRFdfsUpAndDowService drFdfsUpAndDowService;
  56. @Autowired
  57. private DRFdfsUpAndDowService remoteDrFdfsUpAndDowService;
  58. /**
  59. * 获取视频帧数
  60. *
  61. * @param file
  62. * @return
  63. */
  64. public static Double getVideoFrameRate(MultipartFile file) {
  65. try {
  66. FFmpegFrameGrabber frameGrabber = new FFmpegFrameGrabber(file.getInputStream()); // 获取视频文件
  67. frameGrabber.start();
  68. double frameRate = frameGrabber.getFrameRate();
  69. // System.out.println("视频帧数:"+frameGrabber.getFrameRate()); // 视频帧数
  70. // System.out.println("时长为:"+frameGrabber.getLengthInTime() / (1000 * 1000)); // 时长
  71. frameGrabber.close();
  72. return frameRate;
  73. } catch (Exception e) {
  74. e.printStackTrace();
  75. }
  76. return null;
  77. }
  78. /**
  79. * 根据读取到的视频文件,获取视频中的每一帧图片
  80. *
  81. * @param video 视频文件
  82. * http://192.168.0.107:9087/datago/detectsysfiles/204/createFile/frameFile/001/frameFile001.mp4
  83. * @param frameFilePath 图片的保存路径
  84. * E:/detectsysfiles/204/createFile/frameFile/001/
  85. * @param picPath 图像访问路径
  86. * http://192.168.0.107:9087/datago/detectsysfiles/204/createFile/frameFile/001/
  87. */
  88. public static Map<String, Object> getVideoPic(String frameFilePath, File video, String picPath) {
  89. //1.根据一个视频文件,创建一个按照视频中每一帧进行抓取图片的抓取对象
  90. FFmpegFrameGrabber ff = new FFmpegFrameGrabber(video);
  91. Map<String, Object> strMap = new HashMap<>();
  92. List<String> strList = new ArrayList<>();
  93. try {
  94. ff.start();
  95. //抓每一帧图片
  96. //2.先知道这个视频一共有多少帧
  97. int length = ff.getLengthInFrames();
  98. //3.读取视频中每一帧图片
  99. Frame frame = null;
  100. for (int i = 1; i < length; i++) {
  101. frame = ff.grabFrame();
  102. if (frame.image == null) {
  103. continue;
  104. }
  105. //将获取的帧,存储为图片
  106. Java2DFrameConverter converter = new Java2DFrameConverter();//创建一个帧-->图片的转换器
  107. BufferedImage image = converter.getBufferedImage(frame);//转换
  108. String img = picPath + i + ".png";
  109. File picFile = new File(img);
  110. //将图片保存到目标文件中
  111. ImageIO.write(image, "png", picFile);
  112. strList.add(frameFilePath + "/" + i + ".png");
  113. }
  114. strMap.put("height", ff.getImageHeight());//视频高度
  115. strMap.put("width", ff.getImageWidth());//视频宽度
  116. strMap.put("frame", ff.getFrameRate());//帧率
  117. strMap.put("imgPath", strList);//每一帧图像数据
  118. ff.stop();
  119. } catch (Exception e) {
  120. e.printStackTrace();
  121. }
  122. return strMap;
  123. }
  124. /**
  125. * 获取视频时长
  126. *
  127. * @param multipartFile
  128. * @return
  129. * @throws EncoderException
  130. */
  131. public static int queryVideoDuration(MultipartFile multipartFile) throws EncoderException {
  132. //机算视频时长
  133. File dfile = null;
  134. try {
  135. dfile = File.createTempFile("prefix", "_" + multipartFile.getOriginalFilename());
  136. multipartFile.transferTo(dfile);
  137. } catch (IOException e) {
  138. e.printStackTrace();
  139. }
  140. // 获取视频时长
  141. Encoder encoder = new Encoder();
  142. MultimediaInfo m = encoder.getInfo(dfile);
  143. long ls = m.getDuration() / 1000;
  144. int hour = (int) (ls / 3600);
  145. int minute = (int) (ls % 3600) / 60;
  146. int second = (int) (ls - hour * 3600 - minute * 60);
  147. //删除临时文件
  148. dfile.delete();
  149. return second;
  150. }
  151. public static void approvalFile(MultipartFile filecontent, String path) {
  152. OutputStream os = null;
  153. InputStream inputStream = null;
  154. String fileName = null;
  155. try {
  156. inputStream = filecontent.getInputStream();
  157. fileName = filecontent.getOriginalFilename();
  158. } catch (IOException e) {
  159. e.printStackTrace();
  160. }
  161. try {
  162. // 1K的数据缓冲
  163. byte[] bs = new byte[1024];
  164. // 读取到的数据长度
  165. int len;
  166. // 输出的文件流保存到本地文件
  167. File tempFile = new File(path);
  168. if (!tempFile.exists()) {
  169. tempFile.mkdirs();
  170. }
  171. os = new FileOutputStream(tempFile.getPath() + File.separator + fileName);
  172. // 开始读取
  173. while ((len = inputStream.read(bs)) != -1) {
  174. os.write(bs, 0, len);
  175. }
  176. } catch (IOException e) {
  177. e.printStackTrace();
  178. } catch (Exception e) {
  179. e.printStackTrace();
  180. } finally {
  181. // 完毕,关闭所有链接
  182. try {
  183. os.close();
  184. inputStream.close();
  185. } catch (IOException e) {
  186. e.printStackTrace();
  187. }
  188. }
  189. }
  190. private static BufferedImage rotate(BufferedImage src, int angel) {
  191. int src_width = src.getWidth(null);
  192. int src_height = src.getHeight(null);
  193. int type = src.getColorModel().getTransparency();
  194. Rectangle rect_des = calcRotatedSize(new Rectangle(new Dimension(src_width, src_height)), angel);
  195. BufferedImage bi = new BufferedImage(rect_des.width, rect_des.height, type);
  196. Graphics2D g2 = bi.createGraphics();
  197. g2.translate((rect_des.width - src_width) / 2, (rect_des.height - src_height) / 2);
  198. g2.rotate(Math.toRadians(angel), src_width / 2, src_height / 2);
  199. g2.drawImage(src, 0, 0, null);
  200. g2.dispose();
  201. return bi;
  202. }
  203. private static Rectangle calcRotatedSize(Rectangle src, int angel) {
  204. if (angel >= 90) {
  205. if (angel / 90 % 2 == 1) {
  206. int temp = src.height;
  207. src.height = src.width;
  208. src.width = temp;
  209. }
  210. angel = angel % 90;
  211. }
  212. double r = Math.sqrt(src.height * src.height + src.width * src.width) / 2;
  213. double len = 2 * Math.sin(Math.toRadians(angel) / 2) * r;
  214. double angel_alpha = (Math.PI - Math.toRadians(angel)) / 2;
  215. double angel_dalta_width = Math.atan((double) src.height / src.width);
  216. double angel_dalta_height = Math.atan((double) src.width / src.height);
  217. int len_dalta_width = (int) (len * Math.cos(Math.PI - angel_alpha - angel_dalta_width));
  218. int len_dalta_height = (int) (len * Math.cos(Math.PI - angel_alpha - angel_dalta_height));
  219. int des_width = src.width + len_dalta_width * 2;
  220. int des_height = src.height + len_dalta_height * 2;
  221. return new Rectangle(new Dimension(des_width, des_height));
  222. }
  223. /**
  224. * fileUrl 根据视频在线地址获取文件url按秒截取帧
  225. */
  226. public static List<MultipartFile> getVideoImageByUrl(String onlineFileUrl) {
  227. //导出本地地址
  228. // String filePath="C:\\Users\\wonder\\Desktop\\ai测试图片\\检测\\ai_ability_gateway\\视频截取后\\";
  229. // File folder = new File(filePath);
  230. // if (!folder.exists())
  231. // {
  232. // folder.mkdirs();//如果文件夹不存在则创建
  233. // }
  234. List<MultipartFile> list = new ArrayList<>();
  235. try {
  236. //url编码处理,中文名称会变成百分号编码
  237. String fileUrl = URLDecoder.decode(onlineFileUrl, "utf-8");
  238. MultipartFile multipartFile = drFdfsUpAndDowService.downloadMultipartByUrl(fileUrl);
  239. FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(multipartFile.getInputStream());
  240. //FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(file);
  241. grabber.start();
  242. long ftp = grabber.getLengthInFrames();
  243. Frame frame = null;
  244. int sum = 1;
  245. int count = (int) Math.round(grabber.getFrameRate());
  246. for (int i = 1; i <= ftp; i++) {
  247. frame = grabber.grabImage();
  248. if (i % count == 0) {
  249. String rotate = grabber.getVideoMetadata("rotate");
  250. //创建BufferedImage对象
  251. Java2DFrameConverter converter = new Java2DFrameConverter();
  252. BufferedImage bufferedImage = converter.getBufferedImage(frame);
  253. if (rotate != null) {
  254. //旋转图片
  255. bufferedImage = rotate(bufferedImage, Integer.parseInt(rotate));
  256. }
  257. String newFileName = sum + ".jpeg";
  258. //写入本地
  259. //ImageIO.write(bufferedImage, "jpeg", new File(filePath+newFileName));
  260. //BufferedImage 转化为 ByteArrayOutputStream
  261. ByteArrayOutputStream out = new ByteArrayOutputStream();
  262. ImageIO.write(bufferedImage, "jpeg", out);
  263. //ByteArrayOutputStream 转化为 byte[]
  264. byte[] imageByte = out.toByteArray();
  265. //将 byte[] 转为 MultipartFile
  266. MultipartFile convertMultipartFile = new ConvertToMultipartFile(imageByte, null, newFileName, "jpeg", imageByte.length);
  267. list.add(convertMultipartFile);
  268. sum++;
  269. }
  270. }
  271. grabber.close();
  272. grabber.stop();
  273. } catch (Exception e) {
  274. System.out.printf("获取视频图片失败!", e);
  275. }
  276. return list;
  277. }
  278. /**
  279. * fileUrl 根据视频在线地址获取文件url按秒截取帧
  280. */
  281. public static List<MultipartFile> getVideoImageByUrlTest() {
  282. //导出本地地址
  283. String filePath = "C:\\Users\\wonder\\Desktop\\ai测试图片\\检测\\ai_ability_gateway\\视频截取后\\";
  284. File folder = new File(filePath);
  285. if (!folder.exists()) {
  286. folder.mkdirs();//如果文件夹不存在则创建
  287. }
  288. List<MultipartFile> list = new ArrayList<>();
  289. try {
  290. //File file = new File("C:\\Users\\wonder\\Desktop\\ai测试图片\\检测\\ai_ability_gateway\\test.mp4");
  291. File file = new File("C:\\Users\\wonder\\Desktop\\ai测试图片\\检测\\ai_ability_gateway\\测试视频.mp4");
  292. MultipartFile multipartFile = FileUtil.fileToMultipartFile(file);
  293. //MultipartFile multipartFile = drFdfsUpAndDowService.downloadMultipartByUrl(fileUrl);
  294. FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(multipartFile.getInputStream());
  295. //FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(file);
  296. grabber.start();
  297. long ftp = grabber.getLengthInFrames();
  298. Frame frame = null;
  299. int sum = 1;
  300. int flag = 0;
  301. int count = (int) Math.round(grabber.getFrameRate());
  302. for (int i = 1; i <= ftp; i++) {
  303. frame = grabber.grabImage();
  304. if (i % count == 0) {
  305. String rotate = grabber.getVideoMetadata("rotate");
  306. //创建BufferedImage对象
  307. Java2DFrameConverter converter = new Java2DFrameConverter();
  308. BufferedImage bufferedImage = converter.getBufferedImage(frame);
  309. if (rotate != null) {
  310. //旋转图片
  311. bufferedImage = rotate(bufferedImage, Integer.parseInt(rotate));
  312. }
  313. String newFileName = sum + ".jpeg";
  314. //写入本地
  315. ImageIO.write(bufferedImage, "jpeg", new File(filePath + newFileName));
  316. //BufferedImage 转化为 ByteArrayOutputStream
  317. ByteArrayOutputStream out = new ByteArrayOutputStream();
  318. ImageIO.write(bufferedImage, "jpeg", out);
  319. //ByteArrayOutputStream 转化为 byte[]
  320. byte[] imageByte = out.toByteArray();
  321. //将 byte[] 转为 MultipartFile
  322. MultipartFile convertMultipartFile = new ConvertToMultipartFile(imageByte, null, newFileName, "jpeg", imageByte.length);
  323. list.add(convertMultipartFile);
  324. sum++;
  325. }
  326. }
  327. grabber.close();
  328. grabber.stop();
  329. } catch (Exception e) {
  330. System.out.printf("获取视频图片失败!", e);
  331. }
  332. return list;
  333. }
  334. public static void main(String[] args) {
  335. List<MultipartFile> videoImageByUrl = getVideoImageByUrlTest();
  336. }
  337. @PostConstruct
  338. public void init() {
  339. drFdfsUpAndDowService = remoteDrFdfsUpAndDowService;
  340. }
  341. }

2.获取视频时长

  1. /**
  2. * 获取视频时长
  3. * @param multipartFile
  4. * @return
  5. * @throws EncoderException
  6. */
  7. public static int queryVideoDuration(MultipartFile multipartFile) throws EncoderException {
  8. //机算视频时长
  9. File dfile = null;
  10. try {
  11. dfile = File.createTempFile("prefix", "_" + multipartFile.getOriginalFilename());
  12. multipartFile.transferTo(dfile);
  13. } catch (IOException e) {
  14. e.printStackTrace();
  15. }
  16. // 获取视频时长
  17. Encoder encoder = new Encoder();
  18. MultimediaInfo m = encoder.getInfo(dfile);
  19. long ls = m.getDuration()/1000;
  20. int hour = (int) (ls/3600);
  21. int minute = (int) (ls%3600)/60;
  22. int second = (int) (ls-hour*3600-minute*60);
  23. //删除临时文件
  24. dfile.delete();
  25. return second;
  26. }

3.ConvertToMultipartFile 工具类

  1. package com.wondertek.util.img;
  2. import org.springframework.web.multipart.MultipartFile;
  3. import java.io.*;
  4. public class ConvertToMultipartFile implements MultipartFile {
  5. private byte[] fileBytes;
  6. String name;
  7. String originalFilename;
  8. String contentType;
  9. boolean isEmpty;
  10. long size;
  11. public ConvertToMultipartFile(byte[] fileBytes, String name, String originalFilename, String contentType,
  12. long size) {
  13. this.fileBytes = fileBytes;
  14. this.name = name;
  15. this.originalFilename = originalFilename;
  16. this.contentType = contentType;
  17. this.size = size;
  18. this.isEmpty = false;
  19. }
  20. @Override
  21. public String getName() {
  22. return name;
  23. }
  24. @Override
  25. public String getOriginalFilename() {
  26. return originalFilename;
  27. }
  28. @Override
  29. public String getContentType() {
  30. return contentType;
  31. }
  32. @Override
  33. public boolean isEmpty() {
  34. return isEmpty;
  35. }
  36. @Override
  37. public long getSize() {
  38. return size;
  39. }
  40. @Override
  41. public byte[] getBytes() throws IOException {
  42. return fileBytes;
  43. }
  44. @Override
  45. public InputStream getInputStream() throws IOException {
  46. return new ByteArrayInputStream(fileBytes);
  47. }
  48. @Override
  49. public void transferTo(File dest) throws IOException, IllegalStateException {
  50. new FileOutputStream(dest).write(fileBytes);
  51. }
  52. }

发表评论

表情:
评论列表 (有 0 条评论,96人围观)

还没有评论,来说两句吧...

相关阅读