java 精简使用ffmpeg

xml 复制代码
    <properties>
        <java.version>25</java.version>
        <mybatis-flex.version>1.11.7</mybatis-flex.version>
        <awssdk.version>2.42.21</awssdk.version>
        <javacv.version>1.5.13</javacv.version>
        <ffmpeg.version>8.0.1-${javacv.version}</ffmpeg.version>
        <spring-ai.version>2.0.0</spring-ai.version>
        <spring-ai-session.version>0.6.0</spring-ai-session.version>
    </properties>
     <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacv</artifactId>
            <version>${javacv.version}</version>
        </dependency>
            <profiles>
        <profile>
            <id>window</id>
            <dependencies>
                <dependency>
                    <groupId>org.bytedeco</groupId>
                    <artifactId>ffmpeg</artifactId>
                    <version>${ffmpeg.version}</version>
                    <classifier>windows-x86_64</classifier>
                </dependency>
            </dependencies>
        </profile>
        <profile>
            <id>linux</id>
            <dependencies>
                <dependency>
                    <groupId>org.bytedeco</groupId>
                    <artifactId>ffmpeg</artifactId>
                    <version>${ffmpeg.version}</version>
                    <classifier>linux-x86_64</classifier>
                </dependency>
            </dependencies>
        </profile>
    </profiles>
java 复制代码
package com.kongjs.smo.common.util;

import org.bytedeco.javacv.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

public class FFmpegUtil {
    private static final Logger log = LoggerFactory.getLogger(FFmpegUtil.class);

    public static void captureFirstFrame(String videoPath, String outImgPath, String format) {
        FFmpegFrameGrabber grabber = null;
        Java2DFrameConverter converter = new Java2DFrameConverter();
        try {
            grabber = new FFmpegFrameGrabber(videoPath);
            grabber.start();
            if (grabber.getImageWidth() <= 0 || grabber.getImageHeight() <= 0) {
                return;
            }
            Frame frame = null;
            int tryCount = 0;
            while (tryCount < 100) {
                frame = grabber.grabFrame();
                tryCount++;
                if (frame != null && frame.image != null) {
                    break;
                }
            }
            if (frame == null || frame.image == null) {
                return;
            }
            BufferedImage image = converter.convert(frame);
            ImageIO.write(image, format, new File(outImgPath));
        } catch (FFmpegFrameGrabber.Exception e) {
            // 帧读取器异常
            log.error("FFmpeg解码视频失败:" + e.getMessage());
        } catch (FrameGrabber.Exception e) {
            // FFmpeg 底层解码
            log.error("帧采集器读取异常:" + e.getMessage());
        } catch (IOException e) {
            // 文件流异常
            log.error("文件IO读写失败:" + e.getMessage());
        } finally {
            // 强制释放资源,防止句柄泄漏
            if (grabber != null) {
                try {
                    grabber.stop();
                    grabber.close();
                } catch (Exception ignored) {
                }
            }
        }
    }

    public static void captureFirstFrame(String videoPath, String outImgPath, String format, int thumbWidth, int thumbHeight, int quality) {
        FFmpegFrameGrabber grabber = null;
        FFmpegFrameRecorder recorder = null;
        try {
            grabber = new FFmpegFrameGrabber(videoPath);
            grabber.start();
            if (grabber.getImageWidth() <= 0 || grabber.getImageHeight() <= 0) {
                return;
            }
            Frame frame = null;
            int tryCount = 0;
            while (tryCount < 100) {
                // 抓取关键帧,避免黑屏
                frame = grabber.grabKeyFrame();
                tryCount++;
                if (frame != null && frame.image != null) {
                    break;
                }
            }
            if (frame == null || frame.image == null) {
                return;
            }
            int width = grabber.getImageWidth();
            int height = grabber.getImageHeight();
            recorder = new FFmpegFrameRecorder(outImgPath, width, height);
            recorder.setFormat(format);
            recorder.setVideoQuality(quality);
            recorder.start();
            recorder.record(frame);
        } catch (FFmpegFrameGrabber.Exception e) {
            // 帧读取器异常
            log.error("FFmpeg解码视频失败:" + e.getMessage());
        } catch (FrameGrabber.Exception e) {
            // FFmpeg 底层解码
            log.error("帧采集器读取异常:" + e.getMessage());
        } catch (IOException e) {
            // 文件流异常
            log.error("文件IO读写失败:" + e.getMessage());
        } finally {
            if (recorder != null) {
                try {
                    recorder.close();
                } catch (Exception ignored) {
                }
            }
            // 强制释放资源,防止句柄泄漏
            if (grabber != null) {
                try {
                    grabber.close();
                } catch (Exception ignored) {
                }
            }
        }
    }

    static void main() {
        captureFirstFrame("C:\\Users\\101202104016\\Videos\\Captures\\111111111111111111111111111111.mp4","C:\\Users\\101202104016\\Videos\\Captures\\111111111111111111111111111111.webp",
                "webp",400,400,75);
    }

}
相关推荐
leoZ2311 小时前
AI 辅助开发的五道坎
开发语言·人工智能·视觉检测·bert·php·超分辨率重建·openvino
imaol11 小时前
链表 -- 环链表
java·前端·链表
程序员老陆1 小时前
Qt的QThread::usleep和FFmpeg的libavutil模块的av_usleep哪个精度高一些?
开发语言·qt·ffmpeg·音视频
imaol11 小时前
链表 -- 双向链表
java·前端·链表
2601_963869952 小时前
【计算机毕业设计】基于Java的相框定制系统的设计与实现
java·开发语言·课程设计
名字还没想好☜2 小时前
Go 的 unsafe.Pointer 实战:零拷贝 []byte↔string 转换与三条铁律
开发语言·后端·golang·go·unsafe
于烛3 小时前
为什么hasNext() 对只有一个元素的集合返回 true?90%初学者都有的疑问
java
wuminyu3 小时前
JVM利用io_uring优化堆外内存性能原理剖析
java·linux·c语言·jvm·c++
imaol13 小时前
数据结构---队列
java·数据结构·算法
半亩码田4 小时前
【.NET新特性·第11篇】C# 14 字段属性:告别手写 backing field
java·c#·.net