FFmpegFrameGrabber视频抽帧工具类

Bytedeco

通过视频链接进行关键帧抽取图片,利用FFmpegFrameGrabber对视频流进行抽帧处理。

一、引入POM依赖

        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacv</artifactId>
            <version>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>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacpp</artifactId>
            <version>1.5.8</version>
        </dependency>

二、抽帧代码实现

import lombok.extern.slf4j.Slf4j;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * 视频抽帧工具
 */
@Slf4j
public class VideoFrame {

    public static Map<Integer, InputStream> process(String videoUrl, Integer stepSecond, Integer count, String uuid, String ipProxy) {

        Map<Integer, InputStream> result = null;
        int num = 0;
        while (num < 2) {
            log.info("开始抽帧");
            result = videoUrlIntercept(videoUrl, stepSecond, count, uuid, ipProxy);
            if (result.size() > 0) {
                log.info("uuid:{},第{}次抽帧成功", uuid, num + 1);
                break;
            } else {
                //第一次使用ipProxy不成功就更换
                log.info("uuid:{},第{}次抽帧保存失败,ipProxy:{}", uuid, num + 1, ipProxy);
                ipProxy = null;
                num++;
            }
        }
        return result;
    }

    /**
     * 视频文件边下载边抽帧1秒1帧
     *
     * @param videoUrl   网络视频文件URL
     * @param stepSecond 每隔几秒取一帧,默认1s
     * @param count      需要截取的帧个数
     * @param uuid       uuid
     * @return
     */
    public static Map<Integer, InputStream> videoUrlIntercept(String videoUrl, Integer stepSecond, Integer count, String uuid, String ipProxy) {

        Map<Integer, InputStream> files = new HashMap<>();
        stepSecond = stepSecond == null ? 1 : stepSecond;

        FFmpegFrameGrabber ff = new FFmpegFrameGrabber(videoUrl);
        // 设置超时时间为40秒
        ff.setOption("timeout", "40000000");

        ff.setOption("user_agent", UserAgent.getUserAgent());

        try {
            ff.start();
            long timeLength = ff.getLengthInTime();
            Frame frame = ff.grabImage();
            long startTime = frame.timestamp;
            long timestamp = 0;
            int second = 0;
            int picNum = 0;
            while (timestamp <= timeLength) {
                log.info("uuid:{},抽取第{}帧,video_url:{}",uuid,picNum,videoUrl);
                timestamp = startTime + second * 1000000L;
                ff.setTimestamp(timestamp);
                frame = ff.grabImage();
                if (frame != null) {
                    if (frame.image != null) {
                        InputStream inputStream = doExecuteFrame(frame, picNum);
                        if (inputStream != null) {
                            files.put(picNum, inputStream);
                        }
                        picNum++;
                        if (count != null && picNum == count) {
                            break;
                        }
                    }
                }
                second += stepSecond;
                if(picNum > 60) {
                    break;
                }
            }
            ff.stop();

        } catch (Exception e) {
            log.error("下载抽帧失败,uuid:{},ipPort:{},videoUrl:{},msg:{}", uuid, null, videoUrl, e.getMessage());
            e.printStackTrace();
        }

        return files;
    }

    /**
     * 视频文件指定时间段的帧截取
     *
     * @param videoUrl  视频文件URL
     * @param start     视频开始的帧
     * @param count     需要截取的帧个数
     * @param isAvgTime 在截帧时 是否均匀分布计算时间
     * @return
     */
    public static Map<Integer, InputStream> videoIntercept(String videoUrl, int start, int count, boolean isAvgTime) {
        log.info("开始抽取视频帧数,videoUrl:{}",videoUrl);
        Frame frame = null;
        //<时间, 图片流>
        Map<Integer, InputStream> files = new HashMap<>();
        FFmpegFrameGrabber fFmpegFrameGrabber = new FFmpegFrameGrabber(videoUrl);
        fFmpegFrameGrabber.setOption("timeout", "40000000");

        try {
            fFmpegFrameGrabber.start();
            long frameTime = 1;
            if (isAvgTime) {
                frameTime = fFmpegFrameGrabber.getLengthInTime() / count / 1000000L;
                if (frameTime < 0) {
                    frameTime = 1;
                }
            }
            for (int i = start; i <= count; i++) {
                fFmpegFrameGrabber.setTimestamp(i * frameTime * 1000 * 1000);
                frame = fFmpegFrameGrabber.grabImage();
                InputStream inputStream = doExecuteFrame(frame, i);
                if (inputStream != null) {
                    files.put(i, inputStream);
                }
            }
            fFmpegFrameGrabber.stop();
        } catch (Exception E) {
            log.info("下载的视频抽帧失败,msg:" + E.getMessage());
            E.printStackTrace();
        }
        return files;
    }

    public static InputStream doExecuteFrame(Frame frame, int index) {
        if (frame == null || frame.image == null) {
            return null;
        }
        Java2DFrameConverter converter = new Java2DFrameConverter();
        BufferedImage bi = converter.getBufferedImage(frame);
        InputStream inputStream = bufferedImageToInputStream(bi);

        return inputStream;
    }

    public static InputStream bufferedImageToInputStream(BufferedImage image) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        try {
            ImageIO.write(image, "jpg", os);
            InputStream input = new ByteArrayInputStream(os.toByteArray());
            return input;
        } catch (IOException e) {
        }
        return null;
    }

    public static void main(String[] args) throws IOException {
        String videoUrl = "http://vd2.bdstatic.com/mda-pej1ztfufz8axvtu/360p/h264/1684545921389774683/mda-pej1ztfufz8axvtu.mp4";
        Map<Integer, InputStream> integerInputStreamMap = videoIntercept(videoUrl, 1, 13, false);
        System.out.println(integerInputStreamMap.size());
        for (Integer seconds : integerInputStreamMap.keySet()) {
            InputStream inputStream = integerInputStreamMap.get(seconds);
            String fileName = MD5Util.createMd5(System.currentTimeMillis()+"抖音测试3" + "_" + seconds) + ".jpg";
            String filePath = "/test/01/"+fileName;
            //本地磁盘存储
            String uploadURL = PictureDownload.downloadFrame(fileName, "D:/image" + filePath, inputStream, "douyin", true);

            System.out.println("seconds: " + seconds + ", uploadURL: " + uploadURL);
        }

    }
}

猜你喜欢

转载自blog.csdn.net/u010953816/article/details/131435804