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

打印 上一主题 下一主题

主题 793|帖子 793|积分 2381

用到的maven依靠:
lombok依靠就不贴出来了
  1.   <dependency>
  2.             <groupId>org.bytedeco</groupId>
  3.             <artifactId>ffmpeg-platform</artifactId>
  4.             <version>4.3.2-1.5.5</version>
  5.   </dependency>
  6.    <dependency>
  7.         <groupId>org.apache.commons</groupId>
  8.         <artifactId>commons-lang3</artifactId>
  9.         <version>3.12.0</version>
  10.       </dependency>
  11. <dependency>
  12.             <groupId>cn.hutool</groupId>
  13.             <artifactId>hutool-all</artifactId>
  14.             <version>5.5.2</version>
  15.   </dependency>
复制代码

工具类:
  1. import cn.hutool.core.io.IoUtil;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.apache.commons.lang3.StringUtils;
  4. import org.bytedeco.ffmpeg.avcodec.AVCodec;
  5. import org.bytedeco.ffmpeg.avcodec.AVCodecContext;
  6. import org.bytedeco.ffmpeg.avformat.AVFormatContext;
  7. import org.bytedeco.ffmpeg.global.avcodec;
  8. import org.bytedeco.ffmpeg.global.avformat;
  9. import org.bytedeco.ffmpeg.global.avutil;
  10. import org.bytedeco.javacpp.Loader;
  11. import org.bytedeco.javacpp.PointerPointer;
  12. import java.io.*;
  13. import java.util.concurrent.TimeUnit;
  14. @Slf4j
  15. public class VideoUtils {
  16.     static class LazyFfmpeg {
  17.         private static final String ffmpeg = Loader.load(org.bytedeco.ffmpeg.ffmpeg.class);
  18.     }
  19.     public static String ffmpeg() {
  20.         return LazyFfmpeg.ffmpeg;
  21.     }
  22.     /**
  23.      * 压缩视频
  24.      * @param inputFilePath 压缩前视频地址
  25.      * @param outputFilePath 压缩后视频地址
  26.      */
  27.     public static void compressVideo(String inputFilePath, String outputFilePath) {
  28.         if (StringUtils.isAnyBlank(inputFilePath, outputFilePath)) {
  29.             throw new RuntimeException("输入视频路径或输出视频文件路径不能为空");
  30.         }
  31.         if (StringUtils.equals(inputFilePath, outputFilePath)) {
  32.             throw new RuntimeException("outputFilePath不能和inputFilePath相同");
  33.         }
  34.         validIsFile(new File(inputFilePath));
  35.         ProcessBuilder processBuilder = new ProcessBuilder(
  36.                 ffmpeg(),
  37.                 "-y",                     // 自动覆盖输出文件
  38.                 "-i", inputFilePath,      // 输入文件路径
  39.                 "-crf","30",
  40.                 "-c:v","h264",
  41.                 "-preset", "slow",            // 使用较慢的预设来提高压缩效率
  42. //                "-b:v", "1000",  // 设置视频比特率为 1000 kbps
  43. //                "-vf", String.format("scale=%s:%s", 1920, 1080),
  44. //                "-c:a", "copy",           // 保持音频编码不变
  45.                 "-c:a", "aac",                  // 使用 AAC 音频编码
  46.                 "-b:a", "2k",                 // 设置音频比特率为 128 kbps
  47.                 outputFilePath            // 输出文件路径
  48.         );
  49.         StringBuilder stringBuilder = new StringBuilder();
  50.         int exitCode;
  51.         try {
  52.             Process process = processBuilder.start();
  53.             // 捕获错误输出
  54.             processErrorMsg(process, stringBuilder);
  55.             // 等待 FFmpeg 进程完成
  56.             exitCode = process.waitFor();
  57.         } catch (Throwable e) {
  58.             throw new RuntimeException(e);
  59.         }
  60.         if (exitCode != 0) {
  61.             throw new RuntimeException(stringBuilder.toString());
  62.         }
  63.     }
  64.     /**
  65.      * 提取图片
  66.      *
  67.      * @param videoPath 视频路径
  68.      * @param second    提取指定时间图片
  69.      * @param timeout   等待的最长时间
  70.      * @param unit      参数的时间 timeout 单位
  71.      * @return 图片
  72.      */
  73.     public static byte[] ffmpegExtractImage(String videoPath, Number second, long timeout, TimeUnit unit) {
  74.         if (timeout <= 0) {
  75.             throw new IllegalArgumentException("timeout不能小于等于0");
  76.         }
  77.         if (second == null) {
  78.             second = 0;
  79.         }
  80.         if (unit == null) {
  81.             unit = TimeUnit.MINUTES;
  82.         }
  83.         File videoFile = new File(videoPath);
  84.         validIsFile(videoFile);
  85.         ProcessBuilder extractBuilder = new ProcessBuilder(
  86.                 ffmpeg(),
  87.                 "-ss", second.toString(),
  88.                 "-i", videoPath,
  89.                 "-f", "image2pipe",
  90.                 "-vframes", "1",
  91. //                "-vcodec", "png",//如果觉得照片不清晰,就启用此选项,但是照片会变大
  92.                 "-"
  93.         );
  94.         try {
  95.             Process process = extractBuilder.start();
  96.             try (InputStream inputStream = process.getInputStream()) {
  97.                 byte[] bytes = IoUtil.readBytes(inputStream);
  98.                 boolean result = process.waitFor(timeout, unit);
  99.                 if (!result) {
  100.                     throw new RuntimeException("子进程退出之前已超过等待时间");
  101.                 }
  102.                 return bytes;
  103.             }
  104.         } catch (IOException | InterruptedException e) {
  105.             throw new RuntimeException(e);
  106.         }
  107.     }
  108.     /**
  109.      * 获取视频分辨率
  110.      *
  111.      * @param videoFilePath 视频路径
  112.      */
  113.     public static int[] getVideoResolution(String videoFilePath) {
  114.         validIsFile(new File(videoFilePath));
  115.         AVFormatContext formatContext = avformat.avformat_alloc_context();
  116.         AVCodecContext codecContext = avcodec.avcodec_alloc_context3(null);
  117.         // 打开视频文件
  118.         if (avformat.avformat_open_input(formatContext, videoFilePath, null, null) != 0) {
  119.             throw new RuntimeException("无法打开视频文件");
  120.         }
  121.         // 获取视频流信息
  122.         if (avformat.avformat_find_stream_info(formatContext, (PointerPointer) null) < 0) {
  123.             throw new RuntimeException("无法获取视频流信息");
  124.         }
  125.         // 查找视频流
  126.         int videoStreamIndex = -1;
  127.         for (int i = 0; i < formatContext.nb_streams(); i++) {
  128.             if (formatContext.streams(i).codecpar().codec_type() == avutil.AVMEDIA_TYPE_VIDEO) {
  129.                 videoStreamIndex = i;
  130.                 break;
  131.             }
  132.         }
  133.         if (videoStreamIndex == -1) {
  134.             throw new RuntimeException("视频流未找到");
  135.         }
  136.         // 获取视频解码器上下文
  137.         avcodec.avcodec_parameters_to_context(codecContext, formatContext.streams(videoStreamIndex).codecpar());
  138.         // 查找解码器
  139.         AVCodec codec = avcodec.avcodec_find_decoder(codecContext.codec_id());
  140.         if (codec == null) {
  141.             throw new RuntimeException("无法找到解码器");
  142.         }
  143.         // 打开解码器
  144.         if (avcodec.avcodec_open2(codecContext, codec, (PointerPointer) null) < 0) {
  145.             throw new RuntimeException("无法打开解码器");
  146.         }
  147.         // 获取视频分辨率
  148.         int width = codecContext.width();
  149.         int height = codecContext.height();
  150.         // 清理资源
  151.         codecContext.close();
  152.         return new int[]{width, height};
  153.     }
  154.     private static void processErrorMsg(Process process, StringBuilder stringBuilder) {
  155.         new Thread(() -> {
  156.             try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
  157.                 String line;
  158.                 while ((line = reader.readLine()) != null) {
  159.                     stringBuilder.append(line);
  160.                 }
  161.             } catch (IOException e) {
  162.                 log.error("打印命令行错误日志出现异常  errMsg:{}", e.getMessage(), e);
  163.             }
  164.         }).start();
  165.     }
  166.     public static void validIsFile(File file) {
  167.         validExists(file);
  168.         if (!file.isFile()) {
  169.             throw new IllegalArgumentException("不是文件");
  170.         }
  171.     }
  172.     public static void validExists(File file) {
  173.         if (!file.exists()) {
  174.             throw new IllegalArgumentException("videoPath不存在");
  175.         }
  176.     }
  177. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

拉不拉稀肚拉稀

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表