• Java实现截取视频第一帧


    目录

    前言

    一、通过Java借助第三方库实现

    1.引用ffmpeg

    使用maven,导入pom依赖:

     工具类

    2.引用jcodec

    二、使用第三方存储自带的方法实现(如阿里云OSS、华为云OBS)


    前言

    在实际项目中,会遇到上传视频后,需要截取视频的首帧或指定帧为图片,作为展示使用的需求。这个需求本身并不难,而且网上一搜一大把,今天就针对网上的部分方法做个总结。


    一、通过Java借助第三方库实现

    1.引用ffmpeg

    • 使用maven,导入pom依赖:

    1. <dependency>
    2. <groupId>org.bytedecogroupId>
    3. <artifactId>javacppartifactId>
    4. <version>1.4.1version>
    5. dependency>
    6. <dependency>
    7. <groupId>org.bytedecogroupId>
    8. <artifactId>javacvartifactId>
    9. <version>1.4.1version>
    10. dependency>
    11. <dependency>
    12. <groupId>org.bytedeco.javacpp-presetsgroupId>
    13. <artifactId>opencv-platformartifactId>
    14. <version>3.4.1-1.4.1version>
    15. dependency>
    16. <dependency>
    17. <groupId>org.bytedeco.javacpp-presetsgroupId>
    18. <artifactId>ffmpeg-platformartifactId>
    19. <version>3.4.2-1.4.1version>
    20. dependency>
    •  工具类

    1. import org.bytedeco.javacpp.opencv_core;
    2. import org.bytedeco.javacpp.opencv_core.IplImage;
    3. import org.bytedeco.javacv.FFmpegFrameGrabber;
    4. import org.bytedeco.javacv.Frame;
    5. import org.bytedeco.javacv.FrameGrabber.Exception;
    6. import org.bytedeco.javacv.Java2DFrameConverter;
    7. import org.bytedeco.javacv.OpenCVFrameConverter;
    8. import javax.imageio.ImageIO;
    9. import java.awt.image.BufferedImage;
    10. import java.io.File;
    11. import java.io.IOException;
    12. import lombok.extern.slf4j.Slf4j;
    13. /**
    14. * 视频操作工具类
    15. */
    16. @Slf4j
    17. public class VideoUtils {
    18. /**
    19. * 截取视频第一帧为图片展示
    20. *
    21. * @param filePath 视频路径
    22. * @param targetFilePath 第一帧图片存储位置
    23. * @param targetFileName 图片名称
    24. */
    25. public static void getVideoFirstFrameImage(String filePath, String targetFilePath, String targetFileName) throws Exception {
    26. FFmpegFrameGrabber ff = FFmpegFrameGrabber.createDefault(filePath);
    27. ff.start();
    28. String rotate = ff.getVideoMetadata("rotate");
    29. Frame f;
    30. int i = 0;
    31. while (i < 1) {
    32. f = ff.grabImage();
    33. IplImage src;
    34. if (null != rotate && rotate.length() > 1) {
    35. OpenCVFrameConverter.ToIplImage converter = new OpenCVFrameConverter.ToIplImage();
    36. src = converter.convert(f);
    37. f = converter.convert(rotate(src, Integer.parseInt(rotate)));
    38. }
    39. doExecuteFrame(f, targetFilePath, targetFileName);
    40. i++;
    41. }
    42. ff.stop();
    43. }
    44. /**
    45. * 进行旋转角度操作(为了保证截取到的第一帧图片与视频中的角度方向保持一致)
    46. */
    47. public static IplImage rotate(IplImage src, int angle) {
    48. IplImage img = IplImage.create(src.height(), src.width(), src.depth(), src.nChannels());
    49. opencv_core.cvTranspose(src, img);
    50. opencv_core.cvFlip(img, img, angle);
    51. return img;
    52. }
    53. public static void doExecuteFrame(Frame f, String targetFilePath, String targetFileName) {
    54. if (null == f || null == f.image) {
    55. return;
    56. }
    57. Java2DFrameConverter converter = new Java2DFrameConverter();
    58. String imageMat = "jpg";
    59. String fileName = targetFilePath + File.separator + targetFileName + "." + imageMat;
    60. BufferedImage bi = converter.getBufferedImage(f);
    61. File output = new File(fileName);
    62. try {
    63. ImageIO.write(bi, imageMat, output);
    64. } catch (IOException e) {
    65. e.printStackTrace();
    66. }
    67. }
    68. public static void main(String[] args) throws Exception {
    69. String url = "C:\\Users\\admin\\Desktop\\test.mp4";
    70. getVideoFirstFrameImage(url, "C:\\Users\\admin\\Desktop", "first");
    71. }
    72. }

    2.引用jcodec

            网上视频截取第一帧的案例还是比较多的,普遍的方法主要是使用ffmpeg对其截取。在实践过程中,发现在自己本地或在window上截取都是成功的。但在Linux 环境中截取失败,这里将失败内容贴出来,供他人查看失败原因

    1. #
    2. # A fatal error has been detected by the Java Runtime Environment:
    3. #
    4. # SIGSEGV (0xb) at pc=0x000000000000d1f6, pid=14, tid=0x00007fe8b67e6b10
    5. #
    6. # JRE version: OpenJDK Runtime Environment (8.0_212-b04) (build 1.8.0_212-b04)
    7. # Java VM: OpenJDK 64-Bit Server VM (25.212-b04 mixed mode linux-amd64 compressed oops)
    8. # Derivative: IcedTea 3.12.0
    9. # Distribution: Custom build (Sat May 4 17:33:35 UTC 2019)
    10. # Problematic frame:
    11. # C 0x000000000000d1f6
    12. #
    13. # Core dump written. Default location: //core or core.14
    14. #
    15. # If you would like to submit a bug report, please include
    16. # instructions on how to reproduce the bug and visit:
    17. # https://icedtea.classpath.org/bugzilla
    18. # The crash happened outside the Java Virtual Machine in native code.
    19. # See problematic frame for where to report the bug.
    20. #
    21. --------------- T H R E A D ---------------
    22. Current thread (0x00005565ead9c800): JavaThread "http-nio-8889-exec-9" daemon [_thread_in_native, id=109, stack(0x00007fe8b66e6000,0x00007fe8b67e6ad0)]
    23. siginfo: si_signo: 11 (SIGSEGV), si_code: 1 (SEGV_MAPERR), si_addr: 0x000000000000d1f6
    24. (省略部分)
    25. Stack: [0x00007fe8b66e6000,0x00007fe8b67e6ad0], sp=0x00007fe8b67e1558, free space=1005k
    26. Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
    27. C 0x000000000000d1f6
    28. Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
    29. j org.bytedeco.javacpp.avformat.avformat_open_input(Lorg/bytedeco/javacpp/avformat$AVFormatContext;Ljava/lang/String;Lorg/bytedeco/javacpp/avformat$AVInputFormat;Lorg/bytedeco/javacpp/avutil$AVDictionary;)I+0
    30. j org.bytedeco.javacv.FFmpegFrameGrabber.startUnsafe()V+624
    31. j org.bytedeco.javacv.FFmpegFrameGrabber.start()V+1
    32. j com.megvii.qingqiu.middle.platform.provider.util.VideoFrameUtil.fetchFrame(Ljava/io/File;Ljava/io/File;)V+26
    33. j com.megvii.qingqiu.middle.platform.provider.util.VideoFrameUtil.fetchFrame(Lorg/springframework/web/multipart/MultipartFile;)Ljava/io/File;+148
    34. j
    35. (省略部分)

     现在将 ffmpeg 截取的方式改为 jcodecjcodec-javase 来处理。

    • 导入pom依赖:
    1. <dependency>
    2. <groupId>org.jcodecgroupId>
    3. <artifactId>jcodecartifactId>
    4. <version>0.2.5version>
    5. dependency>
    6. <dependency>
    7. <groupId>org.jcodecgroupId>
    8. <artifactId>jcodec-javaseartifactId>
    9. <version>0.2.5version>
    10. dependency>
    •  工具类
    1. import lombok.extern.slf4j.Slf4j;
    2. import org.apache.commons.io.FileUtils;
    3. import org.jcodec.api.FrameGrab;
    4. import org.jcodec.api.JCodecException;
    5. import org.jcodec.common.model.Picture;
    6. import org.jcodec.scale.AWTUtil;
    7. import org.springframework.web.multipart.MultipartFile;
    8. import javax.imageio.ImageIO;
    9. import java.awt.image.BufferedImage;
    10. import java.io.File;
    11. import java.io.IOException;
    12. import java.util.UUID;
    13. /**
    14. * 视频操作工具类
    15. */
    16. @Slf4j
    17. public class VideoUtils {
    18. /*** 图片格式*/
    19. private static final String FILE_EXT = "jpg";
    20. /*** 帧数*/
    21. private static final int THUMB_FRAME = 5;
    22. /**
    23. * 获取指定视频的帧并保存为图片至指定目录
    24. *
    25. * @param videoFilePath 源视频文件路径
    26. * @param frameFilePath 截取帧的图片存放路径
    27. */
    28. public static void fetchFrame(String videoFilePath, String frameFilePath) throws Exception {
    29. File videoFile = new File(videoFilePath);
    30. File frameFile = new File(frameFilePath);
    31. getThumbnail(videoFile, frameFile);
    32. }
    33. /**
    34. * 获取指定视频的帧并保存为图片至指定目录
    35. *
    36. * @param videoFile 源视频文件
    37. * @param targetFile 截取帧的图片
    38. */
    39. public static void fetchFrame(MultipartFile videoFile, File targetFile) throws Exception {
    40. File file = new File(videoFile.getName());
    41. FileUtils.copyInputStreamToFile(videoFile.getInputStream(), file);
    42. getThumbnail(file, targetFile);
    43. }
    44. /**
    45. * 获取指定视频的帧并保存为图片至指定目录
    46. *
    47. * @param videoFile 源视频文件
    48. */
    49. public static File fetchFrame(MultipartFile videoFile) {
    50. String originalFilename = videoFile.getOriginalFilename();
    51. File file = new File(originalFilename);
    52. File targetFile = null;
    53. try {
    54. FileUtils.copyInputStreamToFile(videoFile.getInputStream(), file);
    55. int i = originalFilename.lastIndexOf(".");
    56. String imageName;
    57. if (i > 0) {
    58. imageName = originalFilename.substring(0, i);
    59. } else {
    60. imageName = UUID.randomUUID().toString().replace("-", "");
    61. }
    62. imageName = imageName + ".jpg";
    63. targetFile = new File(imageName);
    64. getThumbnail(file, targetFile);
    65. } catch (Exception e) {
    66. log.error("获取视频指定帧异常:", e);
    67. } finally {
    68. if (file.exists()) {
    69. file.delete();
    70. }
    71. }
    72. log.debug("视频文件 - 帧截取 - 处理结束");
    73. return targetFile;
    74. }
    75. /**
    76. * 获取第一帧缩略图
    77. *
    78. * @param videoFile 视频路径
    79. * @param targetFile 缩略图目标路径
    80. */
    81. public static void getThumbnail(File videoFile, File targetFile) {
    82. try {
    83. // 根据扩展名创建一个新文件路径
    84. Picture picture = FrameGrab.getFrameFromFile(videoFile, THUMB_FRAME);
    85. BufferedImage bufferedImage = AWTUtil.toBufferedImage(picture);
    86. ImageIO.write(bufferedImage, FILE_EXT, targetFile);
    87. } catch (IOException | JCodecException e) {
    88. e.printStackTrace();
    89. log.error("获取第一帧缩略图异常:", e);
    90. }
    91. }
    92. public static void main(String[] args) {
    93. try {
    94. long startTime = System.currentTimeMillis();
    95. getThumbnail(new File("C:\\Users\\admin\\Desktop\\test.mp4"), new File("C:\\Users\\admin\\Desktop\\test.jpg"));
    96. System.out.println("截取图片耗时:" + (System.currentTimeMillis() - startTime));
    97. } catch (Exception e) {
    98. e.printStackTrace();
    99. }
    100. }
    101. }

    二、使用第三方存储自带的方法实现(如阿里云OSS、华为云OBS)

    如果我们的视频是上传并保存在第三方服务器上的,那么,我们可以使用阿里提供的视频截帧方法,根据url直接截取视频的指定帧。
    具体方法就是在视频的 url 后面,加上一些参数,然后访问这个新的url即可得到指定的截图。
    以下是以阿里云OSS为例:

    1. 要加入的参数:
    2. "?x-oss-process=video/snapshot,t_0,f_jpg,w_0,h_0,m_fast,ar_auto"
    3. 新的url:
    4. "https://gnd.oss-cn-zhangjiakou.aliyuncs.com/vidio/test.map?x-oss-process=video/snapshot,t_0,f_jpg,w_0,h_0,m_fast,ar_auto" (直接访问这个新的url即可得到截图)

    参数说明 

    注意事项

    • 仅支持对视频编码格式为H264和H265的视频文件进行视频截帧。
    • OSS默认不保存视频截帧的图片,视频截帧的图片需手动下载并保存至本地。

    亲测了一下,主流的视频格式,如:mp4、mov、avi等均可以采用这种方式进行截帧。

    具体操作请参见阿里官方文档:阿里云视频截帧操作文档

    其它的第三方的云存储跟阿里云的用法,大同小异,根据各自的说明文档,进行开发就行。

    如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、评论、收藏➕关注,您的支持是我坚持写作最大的动力。 

  • 相关阅读:
    数据库(MySQL)的存储过程
    OpenAI的规模化经济效益与第二护城河
    Mac电脑交互式原型设计 Axure RP 8汉化最新 for mac
    Linux命令200例:dip用于用户与远程主机建立通信连接
    vue2深入响应式
    图像融合--挑战、机遇与对策
    八、kotlin的高阶函数
    java如何变更字符串大小写
    等保二级测评国家收费标准是多少?统一的吗?
    在项目中如何直接使用hystrix?
  • 原文地址:https://blog.csdn.net/weixin_42555014/article/details/132982585