Java h265视频抽帧提取照片支持Window,Linux

1. Windows下可调用ffmpeg.exe实现,亲测可行

  • 可以直接执行命令行
# 把目录下视频——E:\\data\\test.h265抽帧每一帧为图片,并命名为1.jpg,2.jpg,3.jpg...存储在E:\\data\\test\\images\\文件夹
ffmpeg.exe -y -i E:\\data\\test.h265 -ss 00:00:00 -f image2 -vsync 2 E:\\data\\test\\images\\%d.jpg

前提需要目录E:\data\test\images\存在;

  • 可以Java ProcessBuilder调用如上命令行实现;

2. linux下调用FFmpegFrameGrabber

  • 调用跨平台的ffmpeg
  • python代码打包成linux平台可调用的二进制文件
  • 推荐优雅的方案:FFmpegFrameGrabber

3. 源码

包括

  • ffmpeg.exe windows支持,linux需要自己配置ffmpeg
  • FFmpegFrameGrabber,跨平台,支持windows,支持linux
    俩个的实现源码如下:
package com.video.extract.test;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.bytedeco.javacv.*;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Arrays;

/*************************************
 *Class Name: VideoUtils
 *Description: <视频抽帧类>
 *@author: Seminar
 *@create: 2021/1/28
 *@since 1.0.0
 *************************************/
@Slf4j
public class VideoUtils {
    
    

    /**
     * 抽帧程序
     * <p>
     * ffmpeg.exe -y -i data\test.H265 -ss 00:00:00 -f image2 -vsync 2 data\test\image\%d.jpg
     *
     * @param videoPath 原始视频文件路径
     * @param imgPath   抽帧图片存储路径
     * @throws IOException
     */
    public static void ffmpegFrameExtract(String videoPath, String imgPath) throws IOException {
    
    
        if (StringUtils.isEmpty(videoPath)) {
    
    
            return;
        }
        File imgPathFolder = new File(imgPath);
        if (!imgPathFolder.exists()) {
    
    
            imgPathFolder.mkdirs();
        } else {
    
    
            delDir(imgPathFolder);
            imgPathFolder.mkdirs();
        }
        String path = System.getProperty("user.dir") + File.separator + "frameExtract" + File.separator + "ffmpeg.exe";
        ProcessBuilder processBuilder = new ProcessBuilder(path, "-y",
                "-i", videoPath,
                "-ss", "00:00:00",
                "-f", "image2",
                "-vsync", "2",
                imgPath + "%d.jpg");
        processBuilder.redirectErrorStream(true);
        Process process = processBuilder.start();
        StringBuilder processOutput = new StringBuilder();
        try (BufferedReader processOutputReader = new BufferedReader(
                new InputStreamReader(process.getInputStream()));) {
    
    
            String readLine;
            while ((readLine = processOutputReader.readLine()) != null) {
    
    
                processOutput.append(readLine + System.lineSeparator());
            }
            process.waitFor();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            if (process != null) {
    
    
                process.destroy();
            }
        }

        // 只返回抽帧的最后3行总览信息
        String[] str = processOutput.toString().trim().split("\n");
        String[] res = new String[3];
        System.arraycopy(str, str.length - 3, res, 0, 3);
        log.info("ffmpegFrameExtract res: {}", String.join("\n", Arrays.asList(res)));
    }

    /**
     * 视频抽帧
     *
     * @param videoPath 原始视频文件路径
     * @param imagePath 抽帧图片存储路径
     * @throws FrameGrabber.Exception
     * @throws FileNotFoundException
     */
    public static void ffmpegFrameGrabber(String videoPath, String imagePath) throws FrameGrabber.Exception, FileNotFoundException {
    
    
//        FFmpegFrameGrabber ff = new FFmpegFrameGrabber(videoPath);// 此处可为视频路径,也可为二进制视频流
        FFmpegFrameGrabber ff = new FFmpegFrameGrabber(new FileInputStream(videoPath));// 此处可为视频路径,也可为二进制视频流
//        ff.setAudioCodecName("h265");
        ff.start();
        int ffLength = ff.getLengthInFrames();
        double ffFrameRate = ff.getFrameRate();
        long videoLength = ff.getLengthInTime();
        log.info("\n");
        log.info("ffmpegFrameGrabber: 帧数:{}, 帧率:{},视频长度:", ffLength, ffFrameRate, videoLength);
        Frame f = ff.grabImage();
        int k = 0;
        while (f != null) {
    
    
            Java2DFrameConverter converter = new Java2DFrameConverter();
            String imageMat = "jpg";
            String fileName = imagePath + k + "." + imageMat;
            BufferedImage bi = converter.getBufferedImage(f);
            File output = new File(fileName);
            try {
    
    
                ImageIO.write(bi, imageMat, output);
            } catch (IOException e) {
    
    
                log.error("writeJpgError: {}", e.getMessage());
                e.printStackTrace();
            }
            k++;
            f = ff.grabImage();
        }
        ff.stop();
        log.info("jpgSize: {}", k);
    }

    /**
     * 递归删除多层目录
     *
     * @param file
     */
    public static void delDir(File file) {
    
    
        // 文件直接删除
        if (file.isFile()) {
    
    
            file.delete();
        } else {
    
    
            // 目录,需要递归删除
            for (File sonfile : file.listFiles()) {
    
    
                delDir(sonfile);
            }
        }
        file.delete();
    }

    public static void main(String[] args) throws IOException {
    
    
        String videoPath = "D:\\project\\frame-extract\\doc\\test.h265";
        String imagePath = videoPath.substring(0, videoPath.lastIndexOf(".")).concat(File.separator).concat("images").concat(File.separator);
        
		// ffmpeg.exe抽帧
        ffmpegFrameExtract(videoPath, imagePath);

        // FFmpegFrameGrabber 抽帧
        ffmpegFrameGrabber(videoPath, imagePath);
    }
}

4. 效果图

在这里插入图片描述

5. pom依赖

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.10</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.bytedeco/javacv -->
<dependency>
    <groupId>org.bytedeco</groupId>
    <artifactId>javacv</artifactId>
    <version>1.5</version>
</dependency>
<dependency>
    <groupId>org.bytedeco</groupId>
    <artifactId>javacv-platform</artifactId>
    <version>1.5</version>
</dependency>
<dependency>
    <groupId>org.bytedeco</groupId>
    <artifactId>javacpp</artifactId>
    <version>1.5</version>
</dependency>
<dependency>
    <groupId>org.bytedeco.javacpp-presets</groupId>
    <artifactId>ffmpeg-platform</artifactId>
    <version>4.1-1.4.4</version>
</dependency>

参考:

猜你喜欢

转载自blog.csdn.net/qq_40985985/article/details/113256353