欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java實(shí)現(xiàn)截取視頻第一幀的示例詳解

 更新時(shí)間:2025年03月04日 09:31:23   作者:Javaの甘乃迪  
在實(shí)際項(xiàng)目中,會(huì)遇到上傳視頻后,需要截取視頻的首幀或指定幀為圖片,作為展示使用的需求,下面小編就來為大家介紹一下如何使用Java實(shí)現(xiàn)截取視頻第一幀吧

前言

在實(shí)際項(xiàng)目中,會(huì)遇到上傳視頻后,需要截取視頻的首幀或指定幀為圖片,作為展示使用的需求。這個(gè)需求本身并不難,而且網(wǎng)上一搜一大把,今天就針對(duì)網(wǎng)上的部分方法做個(gè)總結(jié)。

一、通過Java借助第三方庫(kù)實(shí)現(xiàn)

1.引用ffmpeg

使用maven,導(dǎo)入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;
 
/**
 * 視頻操作工具類
 */
@Slf4j
public class VideoUtils {
 
    /**
     * 截取視頻第一幀為圖片展示
     *
     * @param filePath       視頻路徑
     * @param targetFilePath 第一幀圖片存儲(chǔ)位置
     * @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();
    }
 
    /**
     * 進(jìn)行旋轉(zhuǎn)角度操作(為了保證截取到的第一幀圖片與視頻中的角度方向保持一致)
     */
    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

網(wǎng)上視頻截取第一幀的案例還是比較多的,普遍的方法主要是使用ffmpeg對(duì)其截取。在實(shí)踐過程中,發(fā)現(xiàn)在自己本地或在window上截取都是成功的。但在Linux 環(huán)境中截取失敗,這里將失敗內(nèi)容貼出來,供他人查看失敗原因

#
# 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=1005k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C  0x000000000000d1f6
 
Java 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+0
j  org.bytedeco.javacv.FFmpegFrameGrabber.startUnsafe()V+624
j  org.bytedeco.javacv.FFmpegFrameGrabber.start()V+1
j  com.megvii.qingqiu.middle.platform.provider.util.VideoFrameUtil.fetchFrame(Ljava/io/File;Ljava/io/File;)V+26
j  com.megvii.qingqiu.middle.platform.provider.util.VideoFrameUtil.fetchFrame(Lorg/springframework/web/multipart/MultipartFile;)Ljava/io/File;+148
j  
 
(省略部分)

 現(xiàn)在將 ffmpeg 截取的方式改為 jcodec 和 jcodec-javase 來處理。

導(dǎo)入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;
 
/**
 * 視頻操作工具類
 */
@Slf4j
public class VideoUtils {
 
    /*** 圖片格式*/
    private static final String FILE_EXT = "jpg";
 
    /*** 幀數(shù)*/
    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("視頻文件 - 幀截取 - 處理結(jié)束");
        return targetFile;
    }
 
    /**
     * 獲取第一幀縮略圖
     *
     * @param videoFile  視頻路徑
     * @param targetFile 縮略圖目標(biāo)路徑
     */
    public static void getThumbnail(File videoFile, File targetFile) {
        try {
            // 根據(jù)擴(kuò)展名創(chuàng)建一個(gè)新文件路徑
            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("截取圖片耗時(shí):" + (System.currentTimeMillis() - startTime));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 
}

二、使用第三方存儲(chǔ)自帶的方法實(shí)現(xiàn)

例如阿里云OSS、華為云OBS

如果我們的視頻是上傳并保存在第三方服務(wù)器上的,那么,我們可以使用阿里提供的視頻截幀方法,根據(jù)url直接截取視頻的指定幀。

具體方法就是在視頻的 url 后面,加上一些參數(shù),然后訪問這個(gè)新的url即可得到指定的截圖。

以下是以阿里云OSS為例:

要加入的參數(shù):

"?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" (直接訪問這個(gè)新的url即可得到截圖)

參數(shù)說明 

注意事項(xiàng)

  • 僅支持對(duì)視頻編碼格式為H264和H265的視頻文件進(jìn)行視頻截幀。
  • OSS默認(rèn)不保存視頻截幀的圖片,視頻截幀的圖片需手動(dòng)下載并保存至本地。

親測(cè)了一下,主流的視頻格式,如:mp4、mov、avi等均可以采用這種方式進(jìn)行截幀。

具體操作請(qǐng)參見阿里官方文檔:阿里云視頻截幀操作文檔

其它的第三方的云存儲(chǔ)跟阿里云的用法,大同小異,根據(jù)各自的說明文檔,進(jìn)行開發(fā)就行。

以上就是Java實(shí)現(xiàn)截取視頻第一幀的示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Java截取視頻第一幀的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • java正則表達(dá)式的應(yīng)用 java讀取文件并獲取電話號(hào)碼

    java正則表達(dá)式的應(yīng)用 java讀取文件并獲取電話號(hào)碼

    這篇文章主要介紹了java正則表達(dá)式的應(yīng)用,應(yīng)用的內(nèi)容是java讀取文件并獲取電話號(hào)碼,感興趣的小伙伴們可以參考一下
    2015-11-11
  • 關(guān)于Kill指令停掉Java程序的問題

    關(guān)于Kill指令停掉Java程序的問題

    這篇文章主要介紹了Kill指令停掉Java程序的思考,主要探究kill指令和java的關(guān)閉鉤子的問題,需要的朋友可以參考下
    2021-10-10
  • Java?API操作Hdfs的示例詳解

    Java?API操作Hdfs的示例詳解

    這篇文章主要介紹了Java?API操作Hdfs詳細(xì)示例,遍歷當(dāng)前目錄下所有文件與文件夾,可以使用listStatus方法實(shí)現(xiàn)上述需求,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-08-08
  • Spring Boot + Vue 基于 RSA 的用戶身份認(rèn)證加密機(jī)制實(shí)現(xiàn)過程

    Spring Boot + Vue 基于 RSA 的用戶身份

    RSA是一種非對(duì)稱加密算法,適用于用戶身份認(rèn)證加密,本文介紹了基于RSA的用戶身份認(rèn)證加密機(jī)制的實(shí)現(xiàn),包括前端Vue.js使用jsencrypt庫(kù)對(duì)用戶名密碼進(jìn)行加密,后端使用RSA私鑰解密驗(yàn)證用戶憑據(jù),感興趣的朋友跟隨小編一起看看吧
    2024-11-11
  • SpringBoot權(quán)限認(rèn)證Sa-Token的使用總結(jié)

    SpringBoot權(quán)限認(rèn)證Sa-Token的使用總結(jié)

    Sa-Token是一款輕量級(jí)Java權(quán)限認(rèn)證框架,適用于快速搭建權(quán)限系統(tǒng),它提供了豐富的功能,包括登錄認(rèn)證、權(quán)限驗(yàn)證、角色驗(yàn)證、Session會(huì)話管理等,并且具有良好的社區(qū)支持和文檔資源,下面重點(diǎn)給大家介紹SpringBoot權(quán)限認(rèn)證Sa-Token的使用,感興趣的朋友一起看看吧
    2025-02-02
  • 使用Nacos下載、配置、整合項(xiàng)目方式

    使用Nacos下載、配置、整合項(xiàng)目方式

    這篇文章主要介紹了使用Nacos 下載、配置、整合項(xiàng)目方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • Java內(nèi)存溢出的幾個(gè)區(qū)域總結(jié)(注意避坑!)

    Java內(nèi)存溢出的幾個(gè)區(qū)域總結(jié)(注意避坑!)

    內(nèi)存溢出是指應(yīng)用系統(tǒng)中存在無法回收的內(nèi)存或使用的內(nèi)存過多,最終使得程序運(yùn)行要用到的內(nèi)存大于虛擬機(jī)能提供的最大內(nèi)存,下面這篇文章主要給大家介紹了關(guān)于Java內(nèi)存溢出的幾個(gè)區(qū)域,總結(jié)出來給大家提醒注意避坑,需要的朋友可以參考下
    2022-11-11
  • java工程師進(jìn)階之MyBatis延遲加載的使用

    java工程師進(jìn)階之MyBatis延遲加載的使用

    本文是java工程師進(jìn)階篇,主要介紹了java應(yīng)用開發(fā)中MyBatis延遲加載及如何使用,有需要的朋友 可以借鑒參考下,希望能夠有所幫助
    2021-09-09
  • Java并發(fā)系列之CountDownLatch源碼分析

    Java并發(fā)系列之CountDownLatch源碼分析

    這篇文章主要為大家詳細(xì)介紹了Java并發(fā)系列之CountDownLatch源碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-03-03
  • 解決SpringCloud Feign傳對(duì)象參數(shù)調(diào)用失敗的問題

    解決SpringCloud Feign傳對(duì)象參數(shù)調(diào)用失敗的問題

    這篇文章主要介紹了解決SpringCloud Feign傳對(duì)象參數(shù)調(diào)用失敗的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06

最新評(píng)論