发布时间:2025-06-24 18:34:36  作者:北方职教升学中心  阅读量:377



具体方法就是在视频的 url 后面,加上一些参数,然后访问这个新的url即可得到指定的截图。这个需求本身并不难,而且网上一搜一大把,今天就针对网上的部分方法做个总结。华为云OBS)


前言

在实际项目中,会遇到上传视频后,需要截取视频的首帧或指定帧为图片,作为展示使用的需求。

  • OSS默认不保存视频截帧的图片,视频截帧的图片需手动下载并保存至本地。使用第三方存储自带的方法实现(如阿里云OSS、收藏➕关注,您的支持是我坚持写作最大的动力。华为云OBS)

    如果我们的视频是上传并保存在第三方服务器上的,那么,我们可以使用阿里提供的视频截帧方法,根据url直接截取视频的指定帧。
    以下是以阿里云OSS为例:

    要加入的参数:"?x-oss-process=video/snapshot,t_0,f_jpg,w_0,h_0,m_fast,ar_auto"新的url:"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的视频文件进行视频截帧。通过Java借助第三方库实现

      1.引用ffmpeg

      • 使用maven,导入pom依赖:

      <!-- 操作视频流 -->        <dependency>            <groupId>org.bytedeco</groupId>            <artifactId>javacpp</artifactId>            <version>1.4.1</version>        </dependency>        <dependency>            <groupId>org.bytedeco</groupId>            <artifactId>javacv</artifactId>            <version>1.4.1</version>        </dependency>        <dependency>            <groupId>org.bytedeco.javacpp-presets</groupId>            <artifactId>opencv-platform</artifactId>            <version>3.4.1-1.4.1</version>        </dependency>        <dependency>            <groupId>org.bytedeco.javacpp-presets</groupId>            <artifactId>ffmpeg-platform</artifactId>            <version>3.4.2-1.4.1</version>        </dependency>
      •  工具类

      import org.bytedeco.javacpp.opencv_core;import org.bytedeco.javacpp.opencv_core.IplImage;import org.bytedeco.javacv.FFmpegFrameGrabber;import org.bytedeco.javacv.Frame;import org.bytedeco.javacv.FrameGrabber.Exception;import org.bytedeco.javacv.Java2DFrameConverter;import org.bytedeco.javacv.OpenCVFrameConverter;import javax.imageio.ImageIO;import java.awt.image.BufferedImage;import java.io.File;import java.io.IOException;import lombok.extern.slf4j.Slf4j;/** * 视频操作工具类 */@Slf4jpublic class VideoUtils {    /**     * 截取视频第一帧为图片展示     *     * @param filePath       视频路径     * @param targetFilePath 第一帧图片存储位置     * @param targetFileName 图片名称     */    public static void getVideoFirstFrameImage(String filePath, String targetFilePath, String targetFileName) throws Exception {        FFmpegFrameGrabber ff = FFmpegFrameGrabber.createDefault(filePath);        ff.start();        String rotate = ff.getVideoMetadata("rotate");        Frame f;        int i = 0;        while (i < 1) {            f = ff.grabImage();            IplImage src;            if (null != rotate && rotate.length() > 1) {                OpenCVFrameConverter.ToIplImage converter = new OpenCVFrameConverter.ToIplImage();                src = converter.convert(f);                f = converter.convert(rotate(src, Integer.parseInt(rotate)));            }            doExecuteFrame(f, targetFilePath, targetFileName);            i++;        }        ff.stop();    }    /**     * 进行旋转角度操作(为了保证截取到的第一帧图片与视频中的角度方向保持一致)     */    public static IplImage rotate(IplImage src, int angle) {        IplImage img = IplImage.create(src.height(), src.width(), src.depth(), src.nChannels());        opencv_core.cvTranspose(src, img);        opencv_core.cvFlip(img, img, angle);        return img;    }    public static void doExecuteFrame(Frame f, String targetFilePath, String targetFileName) {        if (null == f || null == f.image) {            return;        }        Java2DFrameConverter converter = new Java2DFrameConverter();        String imageMat = "jpg";        String fileName = targetFilePath + File.separator + targetFileName + "." + imageMat;        BufferedImage bi = converter.getBufferedImage(f);        File output = new File(fileName);        try {            ImageIO.write(bi, imageMat, output);        } catch (IOException e) {            e.printStackTrace();        }    }    public static void main(String[] args) throws Exception {        String url = "C:\\Users\\admin\\Desktop\\test.mp4";        getVideoFirstFrameImage(url, "C:\\Users\\admin\\Desktop", "first");    }}

      2.引用jcodec

              网上视频截取第一帧的案例还是比较多的,普遍的方法主要是使用ffmpeg对其截取。

    亲测了一下,主流的视频格式,如:mp4、

    如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、 

    • 导入pom依赖:
    <dependency>            <groupId>org.jcodec</groupId>            <artifactId>jcodec</artifactId>            <version>0.2.5</version>        </dependency>        <dependency>            <groupId>org.jcodec</groupId>            <artifactId>jcodec-javase</artifactId>            <version>0.2.5</version>        </dependency>        <!--<dependency>            <groupId>org.bytedeco</groupId>            <artifactId>javacv</artifactId>            <version>1.4.1</version>        </dependency>        <dependency>            <groupId>org.bytedeco</groupId>            <artifactId>javacpp</artifactId>            <version>1.4.1</version>        </dependency>        <dependency>            <groupId>org.bytedeco.javacpp-presets</groupId>            <artifactId>opencv-platform</artifactId>            <version>3.4.1-1.4.1</version>        </dependency>        <dependency>            <groupId>org.bytedeco.javacpp-presets</groupId>            <artifactId>ffmpeg-platform</artifactId>            <version>3.4.2-1.4.1</version>        </dependency>-->
    •  工具类
    import lombok.extern.slf4j.Slf4j;import org.apache.commons.io.FileUtils;import org.jcodec.api.FrameGrab;import org.jcodec.api.JCodecException;import org.jcodec.common.model.Picture;import org.jcodec.scale.AWTUtil;import org.springframework.web.multipart.MultipartFile;import javax.imageio.ImageIO;import java.awt.image.BufferedImage;import java.io.File;import java.io.IOException;import java.util.UUID;/** * 视频操作工具类 */@Slf4jpublic class VideoUtils {    /*** 图片格式*/    private static final String FILE_EXT = "jpg";    /*** 帧数*/    private static final int THUMB_FRAME = 5;    /**     * 获取指定视频的帧并保存为图片至指定目录     *     * @param videoFilePath 源视频文件路径     * @param frameFilePath 截取帧的图片存放路径     */    public static void fetchFrame(String videoFilePath, String frameFilePath) throws Exception {        File videoFile = new File(videoFilePath);        File frameFile = new File(frameFilePath);        getThumbnail(videoFile, frameFile);    }    /**     * 获取指定视频的帧并保存为图片至指定目录     *     * @param videoFile  源视频文件     * @param targetFile 截取帧的图片     */    public static void fetchFrame(MultipartFile videoFile, File targetFile) throws Exception {        File file = new File(videoFile.getName());        FileUtils.copyInputStreamToFile(videoFile.getInputStream(), file);        getThumbnail(file, targetFile);    }    /**     * 获取指定视频的帧并保存为图片至指定目录     *     * @param videoFile 源视频文件     */    public static File fetchFrame(MultipartFile videoFile) {        String originalFilename = videoFile.getOriginalFilename();        File file = new File(originalFilename);        File targetFile = null;        try {            FileUtils.copyInputStreamToFile(videoFile.getInputStream(), file);            int i = originalFilename.lastIndexOf(".");            String imageName;            if (i > 0) {                imageName = originalFilename.substring(0, i);            } else {                imageName = UUID.randomUUID().toString().replace("-", "");            }            imageName = imageName + ".jpg";            targetFile = new File(imageName);            getThumbnail(file, targetFile);        } catch (Exception e) {            log.error("获取视频指定帧异常:", e);        } finally {            if (file.exists()) {                file.delete();            }        }        log.debug("视频文件 - 帧截取 - 处理结束");        return targetFile;    }    /**     * 获取第一帧缩略图     *     * @param videoFile  视频路径     * @param targetFile 缩略图目标路径     */    public static void getThumbnail(File videoFile, File targetFile) {        try {            // 根据扩展名创建一个新文件路径            Picture picture = FrameGrab.getFrameFromFile(videoFile, THUMB_FRAME);            BufferedImage bufferedImage = AWTUtil.toBufferedImage(picture);            ImageIO.write(bufferedImage, FILE_EXT, targetFile);        } catch (IOException | JCodecException e) {            e.printStackTrace();            log.error("获取第一帧缩略图异常:", e);        }    }    public static void main(String[] args) {        try {            long startTime = System.currentTimeMillis();            getThumbnail(new File("C:\\Users\\admin\\Desktop\\test.mp4"), new File("C:\\Users\\admin\\Desktop\\test.jpg"));            System.out.println("截取图片耗时:" + (System.currentTimeMillis() - startTime));        } catch (Exception e) {            e.printStackTrace();        }    }}

    二、使用第三方存储自带的方法实现(如阿里云OSS、评论、但在Linux环境中截取失败,这里将失败内容贴出来,供他人查看失败原因

    ## A fatal error has been detected by the Java Runtime Environment:##  SIGSEGV (0xb) at pc=0x000000000000d1f6, pid=14, tid=0x00007fe8b67e6b10## JRE version: OpenJDK Runtime Environment (8.0_212-b04) (build 1.8.0_212-b04)# Java VM: OpenJDK 64-Bit Server VM (25.212-b04 mixed mode linux-amd64 compressed oops)# Derivative: IcedTea 3.12.0# Distribution: Custom build (Sat May  4 17:33:35 UTC 2019)# Problematic frame:# C  0x000000000000d1f6## Core dump written. Default location: //core or core.14## If you would like to submit a bug report, please include# instructions on how to reproduce the bug and visit:#   https://icedtea.classpath.org/bugzilla# The crash happened outside the Java Virtual Machine in native code.# See problematic frame for where to report the bug.#---------------  T H R E A D  ---------------Current thread (0x00005565ead9c800):  JavaThread "http-nio-8889-exec-9" daemon [_thread_in_native, id=109, stack(0x00007fe8b66e6000,0x00007fe8b67e6ad0)]siginfo: si_signo: 11 (SIGSEGV), si_code: 1 (SEGV_MAPERR), si_addr: 0x000000000000d1f6(省略部分)Stack: [0x00007fe8b66e6000,0x00007fe8b67e6ad0],  sp=0x00007fe8b67e1558,  free space=1005kNative frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)C  0x000000000000d1f6Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)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+0j  org.bytedeco.javacv.FFmpegFrameGrabber.startUnsafe()V+624j  org.bytedeco.javacv.FFmpegFrameGrabber.start()V+1j  com.megvii.qingqiu.middle.platform.provider.util.VideoFrameUtil.fetchFrame(Ljava/io/File;Ljava/io/File;)V+26j  com.megvii.qingqiu.middle.platform.provider.util.VideoFrameUtil.fetchFrame(Lorg/springframework/web/multipart/MultipartFile;)Ljava/io/File;+148j  (省略部分)

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


    一、在实践过程中,发现在自己本地或在window上截取都是成功的。通过Java借助第三方库实现

    1.引用ffmpeg

    使用maven,导入pom依赖:

     工具类

    2.引用jcodec

    二、avi等均可以采用这种方式进行截帧。

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

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

    目录

    前言

    一、