springboot java ffmpeg 视频压缩、提取视频帧图片、获取视频分辨率

server/2024/12/19 5:13:08/

用到的maven依赖:

lombok依赖就不贴出来了

java">  <dependency><groupId>org.bytedeco</groupId><artifactId>ffmpeg-platform</artifactId><version>4.3.2-1.5.5</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.12.0</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.5.2</version></dependency>

工具类:

java">import cn.hutool.core.io.IoUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.bytedeco.ffmpeg.avcodec.AVCodec;
import org.bytedeco.ffmpeg.avcodec.AVCodecContext;
import org.bytedeco.ffmpeg.avformat.AVFormatContext;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avformat;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacpp.Loader;
import org.bytedeco.javacpp.PointerPointer;import java.io.*;
import java.util.concurrent.TimeUnit;@Slf4j
public class VideoUtils {static class LazyFfmpeg {private static final String ffmpeg = Loader.load(org.bytedeco.ffmpeg.ffmpeg.class);}public static String ffmpeg() {return LazyFfmpeg.ffmpeg;}/*** 压缩视频* @param inputFilePath 压缩前视频地址* @param outputFilePath 压缩后视频地址*/public static void compressVideo(String inputFilePath, String outputFilePath) {if (StringUtils.isAnyBlank(inputFilePath, outputFilePath)) {throw new RuntimeException("输入视频路径或输出视频文件路径不能为空");}if (StringUtils.equals(inputFilePath, outputFilePath)) {throw new RuntimeException("outputFilePath不能和inputFilePath相同");}validIsFile(new File(inputFilePath));ProcessBuilder processBuilder = new ProcessBuilder(ffmpeg(),"-y",                     // 自动覆盖输出文件"-i", inputFilePath,      // 输入文件路径"-crf","30","-c:v","h264","-preset", "slow",            // 使用较慢的预设来提高压缩效率
//                "-b:v", "1000",  // 设置视频比特率为 1000 kbps
//                "-vf", String.format("scale=%s:%s", 1920, 1080),
//                "-c:a", "copy",           // 保持音频编码不变"-c:a", "aac",                  // 使用 AAC 音频编码"-b:a", "2k",                 // 设置音频比特率为 128 kbpsoutputFilePath            // 输出文件路径);StringBuilder stringBuilder = new StringBuilder();int exitCode;try {Process process = processBuilder.start();// 捕获错误输出processErrorMsg(process, stringBuilder);// 等待 FFmpeg 进程完成exitCode = process.waitFor();} catch (Throwable e) {throw new RuntimeException(e);}if (exitCode != 0) {throw new RuntimeException(stringBuilder.toString());}}/*** 提取图片** @param videoPath 视频路径* @param second    提取指定时间图片* @param timeout   等待的最长时间* @param unit      参数的时间 timeout 单位* @return 图片*/public static byte[] ffmpegExtractImage(String videoPath, Number second, long timeout, TimeUnit unit) {if (timeout <= 0) {throw new IllegalArgumentException("timeout不能小于等于0");}if (second == null) {second = 0;}if (unit == null) {unit = TimeUnit.MINUTES;}File videoFile = new File(videoPath);validIsFile(videoFile);ProcessBuilder extractBuilder = new ProcessBuilder(ffmpeg(),"-ss", second.toString(),"-i", videoPath,"-f", "image2pipe","-vframes", "1",
//                "-vcodec", "png",//如果觉得照片不清晰,就启用此选项,但是照片会变大"-");try {Process process = extractBuilder.start();try (InputStream inputStream = process.getInputStream()) {byte[] bytes = IoUtil.readBytes(inputStream);boolean result = process.waitFor(timeout, unit);if (!result) {throw new RuntimeException("子进程退出之前已超过等待时间");}return bytes;}} catch (IOException | InterruptedException e) {throw new RuntimeException(e);}}/*** 获取视频分辨率** @param videoFilePath 视频路径*/public static int[] getVideoResolution(String videoFilePath) {validIsFile(new File(videoFilePath));AVFormatContext formatContext = avformat.avformat_alloc_context();AVCodecContext codecContext = avcodec.avcodec_alloc_context3(null);// 打开视频文件if (avformat.avformat_open_input(formatContext, videoFilePath, null, null) != 0) {throw new RuntimeException("无法打开视频文件");}// 获取视频流信息if (avformat.avformat_find_stream_info(formatContext, (PointerPointer) null) < 0) {throw new RuntimeException("无法获取视频流信息");}// 查找视频流int videoStreamIndex = -1;for (int i = 0; i < formatContext.nb_streams(); i++) {if (formatContext.streams(i).codecpar().codec_type() == avutil.AVMEDIA_TYPE_VIDEO) {videoStreamIndex = i;break;}}if (videoStreamIndex == -1) {throw new RuntimeException("视频流未找到");}// 获取视频解码器上下文avcodec.avcodec_parameters_to_context(codecContext, formatContext.streams(videoStreamIndex).codecpar());// 查找解码器AVCodec codec = avcodec.avcodec_find_decoder(codecContext.codec_id());if (codec == null) {throw new RuntimeException("无法找到解码器");}// 打开解码器if (avcodec.avcodec_open2(codecContext, codec, (PointerPointer) null) < 0) {throw new RuntimeException("无法打开解码器");}// 获取视频分辨率int width = codecContext.width();int height = codecContext.height();// 清理资源codecContext.close();return new int[]{width, height};}private static void processErrorMsg(Process process, StringBuilder stringBuilder) {new Thread(() -> {try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {String line;while ((line = reader.readLine()) != null) {stringBuilder.append(line);}} catch (IOException e) {log.error("打印命令行错误日志出现异常  errMsg:{}", e.getMessage(), e);}}).start();}public static void validIsFile(File file) {validExists(file);if (!file.isFile()) {throw new IllegalArgumentException("不是文件");}}public static void validExists(File file) {if (!file.exists()) {throw new IllegalArgumentException("videoPath不存在");}}
}


http://www.ppmy.cn/server/151365.html

相关文章

list使用

目录 list介绍 list使用 list创建 list迭代器 容量操作 元素访问 修改元素 其他操作 list介绍 ● list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代 ● list的底层是双向链表结构&#xff0c;双向链表中每个元素存…

华为WLAN基础配置(AC6005模拟配置)

AC6005基础配置 本次实验模拟华为AC6005的基本配置 Tip display interface GigabitEthernet 0/0/0 查看ap接口mac 前提条件&#xff1a;Vlan10为业务网段&#xff0c;vlan100为管理网段&#xff0c;5700作为dhcp。 5700配置如下 <Huawei>sy [Huawei]sys 5700 //设…

数据仓库-集群管理

主要介绍操作类问题中的集群管理问题。 无法成功创建数据仓库集群时怎么处理&#xff1f; 请检查用户账户余额是否少于100元&#xff0c;是否已经没有配额创建新的数据仓库集群&#xff0c;以及是否存在网络问题。 如账户余额、配额、网络均未发现问题&#xff0c;请联系客户…

欧科云链研究院:AI时代,如何证明“我是我”?

OKG Research&#xff5c;编辑 近日&#xff0c;OpenAI 发布了新模型 Sora。这是一款高性能的文本到多模态生成工具&#xff0c;支持从文本生成精细的图像和动态视频。 相较早先发布的视频样例&#xff0c;该功能目前已经可以由用户真实上手体验&#xff0c;目前由于服务过载…

【Android】EventBus——进行良好的组件通信

引言 EventBus是一个基于发布/订阅模式的事件总线库。它主要用于Android应用程序中组件之间的通信&#xff0c;允许不同组件&#xff08;如Activity、Fragment、Service等&#xff09;之间进行松耦合的交互。EventBus通过一个中央事件系统来传递消息&#xff0c;这些消息可以是…

14篇--模板匹配

原理 模板匹配就是用模板图&#xff08;通常是一个小图&#xff09;在目标图像&#xff08;通常是一个比模板图大的图片&#xff09;中不断的滑动比较&#xff0c;通过某种比较方法来判断是否匹配成功。 匹配方法 1. 平方差匹配TM_SQDIFF 以模板图与目标图所对应的像素值使用…

概率论得学习和整理25:EXCEL 关于直方图/ 频度图 /hist图的细节,2种做hist图的方法

目录 1 hist图的特点 2 hist的设置技巧&#xff1a;直接生成的hist图往往很奇怪不好用&#xff1a;因为横轴的分组不对 3 如何修改分组 4 设置开放边界&#xff0c;把长尾合并&#xff0c;得到hist图1 5 用原始表得到频数表 6 用上面的频数图做柱状图&#xff0c;再修改&…

蓝桥杯刷题——day4

蓝桥杯刷题——day4 题目一题干题目解析代码 题目二题干题目解析代码 题目一 题干 小蓝和朋友们在玩一个报数游戏。由于今年是2024 年&#xff0c;他们决定要从小到大轮流报出是20或24倍数的正整数。前10个被报出的数是&#xff1a;20,24,40,48,60,72,80,96,100,120。请问第2…