Java程序处理视频裁剪(快速处理大文件)

2023-11-01 18:10

本文主要是介绍Java程序处理视频裁剪(快速处理大文件),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        现在手机上的各种视频裁剪、剪切软件,裁剪视频特别方便。但是遇到大文件需要裁剪的时候,就麻烦了,动辄大几G、十几G的文件就不适合用手机裁剪app了,连上传都成功不了,而且还会被各种压缩,画质失真严重。这个时候,就只能用电脑裁剪了,电脑上还要找付费的视频剪切软件或费时费力找免费软件,就很麻烦了。这里用java程序实现一个视频剪切工具,处理大的视频软件不失真、速度快,轻松搞定大文件视频剪切的需求,非常方便,源码和思路往下看!

文章目录

    • 0.前期准备
    • 1.新建一个java项目
    • 2.代码生成ffmpeg指令
    • 3.视频裁剪任务执行
    • 4.主程序执行效果展示

0.前期准备

下载并安装ffmpeg,如果电脑上已经有了,就可以忽略这个步骤。 ✈ 直达下载安装ffmpeg

找到解压之后的地址,进入bin目录下,可以看到ffmpeg.exe的文件
在这里插入图片描述
工具准备好了,下面开始整代码👇👇👇

1.新建一个java项目

建一个最基本的java项目就行,不需要引第三方的依赖包,jdk自带的方法就够用了,所以不需要建maven项目或springboot项目了。

2.代码生成ffmpeg指令

首先,要验证一下本地的ffmpeg的地址是否存在,只有确实有ffmpeg.exe才可以执行视频裁剪工作。

其次,构造生成ffmpeg的shell指令,用于执行视频裁剪任务。此外,还可以按照自己的规则设置输出文件的名称和地址。

方式一:ffmpeg.exe -ss %s -i %s -t %s -c copy %s -y
ffmpeg.exe -ss 00:00:05 -i test.mp4 -t 00:00:18 -c copy test_new.mp4 -y,说明:从第5秒开始剪辑,裁剪时长为18秒,最终裁剪的时间段应该为(5-23)

方式二:ffmpeg.exe -ss %s -i %s -to %s -c copy %s -y
ffmpeg.exe -ss 00:00:05 -i test.mp4 -to 00:00:18 -c copy test_new.mp4 -y,说明:从第5秒开始剪辑,第18秒为结束时间,最终裁剪的时间段应该为(5-18)

ffmpeg指令参数说明:
ss 起始时间;i 指定要转换的文件;t 裁剪时长;to 截止时间;c 操作方式copy;y 覆盖已存在的文件

踩坑指南:
按照官方的文档说明,正常t是裁剪的时长,to是裁剪的截止时间,但是t和to两种都用了之后,最终结果都是裁剪时长!暂时没找到原因,就先用裁剪时长吧,如果想要用截止时间,简单封装一下也可以

代码贴在这里:

/*** ffmpeg相关工具类*/
public class FFMpegShellUtil {/*** ffmpeg是否存在* @return*/public static boolean isFFMpegReadied() {//电脑本地的ffmpeg的地址String ffmpegPath = "D:\\ffmpeg\\ffmpeg-5.1.2-full_build-shared\\bin\\ffmpeg.exe";File file = new File(ffmpegPath);boolean exists = file.exists();System.out.println("ffmpeg工具准备状态:" + exists);return exists;}/*** 构造ffmpeg指令* @param startTime 开始时间 00:01:00* @param input     源文件 video.mp4* @param durTime   裁剪时长 00:02:00*/public static String createCutShell(String input, String startTime, String durTime) {String shell;//输出文件名称String output = getOutputName(input, startTime, durTime);System.out.println("剪辑输出视频文件的地址:"+output);//系统名称String systemName = getSystemName();//ffmpeg指令参数说明(ss 起始时间 | i 指定要转换的文件 | t 裁剪时长 | c 操作方式,copy | y 覆盖已存在的文件)if (systemName.contains("windows")) { //windows系统shell = String.format("ffmpeg.exe -ss %s -i %s -t %s -c copy %s -y", startTime, input, durTime, output);} else { //其他系统shell = String.format("ffmpeg -ss %s -i %s -t %s -c copy %s -y", startTime, input, durTime, output);}return shell;}/*** 获取输出文件名称* @param input     源文件 video.mp4* @param startTime 开始时间 00:01:00* @param durTime   裁剪时长 00:02:00* @return*/private static String getOutputName(String input, String startTime, String durTime){String[] arr = input.split("\\.");String name = arr[0];String suffix = arr[1];String start = startTime.replaceAll(":","");String dur = durTime.replaceAll(":","");return name+"_"+start+"_"+dur+"."+suffix;}/*** 获取系统平台名称:windows、linux...*/private static String getSystemName() {return System.getProperty("os.name").toLowerCase();}
}

3.视频裁剪任务执行

使用Runtime.getRuntime().exec()的方法执行,主要是Java应用生成一个新的进程调用系统外部的的程序,本质上也就是用来执行ffmpeg的shell指令的。

但是这边有个很大的漏洞,使用waitFor()方法时,由于java给进程的输出流分配的缓冲区很小,特别容易发生缓冲区被填满、程序阻塞的问题,错误输出流缓冲区与标准输出流缓冲区会由于清空不及时发生阻塞,异步开两个线程分别去处理标准输出流和错误输出流,可以有效地解决缓冲区信息阻塞的问题,这里使用了两个线程方法threadExec执行!

/*** 视频裁剪任务*/
public class VideoCutTask {/*** 任务执行* @param shell ffmpeg指令* @return*/public static void exec(String shell) {System.out.println("shell:" + shell);BufferedReader bufferedReader = null;try {Process process = Runtime.getRuntime().exec(shell);//            bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
//            String line;
//            StringBuilder sb = new StringBuilder();
//            while (null != (line = bufferedReader.readLine())) {
//                if (!line.isEmpty()) {
//                    sb.append(line);
//                }
//            }
//            process.waitFor();//使用waitFor()方法时,由于java给进程的输出流分配的缓冲区很小,特别容易发生缓冲区被填满、程序阻塞的问题//使用两个线程分别去处理标准输出流和错误输出流,可以有效地解决缓冲区信息阻塞的问题threadExec(process.getInputStream());threadExec(process.getErrorStream());process.waitFor();System.out.println("shell执行结束!");} catch (IOException e) {e.printStackTrace();System.err.println("shell执行IOException:" + e.getMessage());} catch (InterruptedException e) {e.printStackTrace();System.err.println("shell执行InterruptedException:" + e.getMessage());} finally {if (null != bufferedReader) {try {bufferedReader.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 分线程执行* @param input 输入流*/private static void threadExec(final InputStream input) {new Thread(()->{BufferedReader bf = new BufferedReader(new InputStreamReader(input));String line = null;try{while(null != (line = bf.readLine())) {System.out.println(line);}} catch (IOException e) {e.printStackTrace();}}).start();}
}

4.主程序执行效果展示

main方法处直接执行看生成的效果,需要传入的参数有:【视频源文件路径】、【剪辑开始时间】、【剪辑时长】三个参数,默认输出的文件与源文件放在同一目录下,命名方式不同做区分。

public class Main {public static void main(String[] args) {//视频源文件路径String inputPath = "D:\\videoTest\\trailer.mp4";//剪辑开始时间String start = "00:00:11";//剪辑时长String duration = "00:00:25";//验证ffmpeg是否就绪if (FFMpegShellUtil.isFFMpegReadied()) {//创建ffmpeg裁剪指令String shell = FFMpegShellUtil.createCutShell(inputPath, start, duration);//执行剪辑操作VideoCutTask.exec(shell);}}
}

最后执行的效果,可以看到当前路径下生成一个时长25秒的视频,就是裁剪后的视频,如下图
在这里插入图片描述

试着裁剪了个14G的大视频,裁取中间的一段视频,基本都是在秒级时间内完成的,速度非常的快。可以看一下裁剪的日志:

Connected to the target VM, address: '127.0.0.1:49712', transport: 'socket'
ffmpeg工具准备状态:true
剪辑输出视频文件的地址:E:\sjc_011210_000125.mp4
shell:ffmpeg.exe -ss 01:12:10 -i E:\sjc.mp4 -t 00:01:25 -c copy E:\sjc_011210_000125.mp4 -y
ffmpeg version 5.1.2-full_build-www.gyan.dev Copyright (c) 2000-2022 the FFmpeg developersbuilt with gcc 12.1.0 (Rev2, Built by MSYS2 project)configuration: --enable-gpl --enable-version3 --enable-shared --disable-w32threads --disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-libxml2 --enable-gmp --enable-bzlib --enable-lzma --enable-libsnappy --enable-zlib --enable-librist --enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth --enable-libbluray --enable-libcaca --enable-sdl2 --enable-libaribb24 --enable-libdav1d --enable-libdavs2 --enable-libuavs3d --enable-libzvbi --enable-librav1e --enable-libsvtav1 --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxavs2 --enable-libxvid --enable-libaom --enable-libjxl --enable-libopenjpeg --enable-libvpx --enable-mediafoundation --enable-libass --enable-frei0r --enable-libfreetype --enable-libfribidi --enable-liblensfun --enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm --enable-cuvid --enable-ffnvcodec --enable-nvdec --enable-nvenc --enable-d3d11va --enable-dxva2 --enable-libmfx --enable-libshaderc --enable-vulkan --enable-libplacebo --enable-opencl --enable-libcdio --enable-libgme --enable-libmodplug --enable-libopenmpt --enable-libopencore-amrwb --enable-libmp3lame --enable-libshine --enable-libtheora --enable-libtwolame --enable-libvo-amrwbenc --enable-libilbc --enable-libgsm --enable-libopencore-amrnb --enable-libopus --enable-libspeex --enable-libvorbis --enable-ladspa --enable-libbs2b --enable-libflite --enable-libmysofa --enable-librubberband --enable-libsoxr --enable-chromaprintlibavutil      57. 28.100 / 57. 28.100libavcodec     59. 37.100 / 59. 37.100libavformat    59. 27.100 / 59. 27.100libavdevice    59.  7.100 / 59.  7.100libavfilter     8. 44.100 /  8. 44.100libswscale      6.  7.100 /  6.  7.100libswresample   4.  7.100 /  4.  7.100libpostproc    56.  6.100 / 56.  6.100
[mov,mp4,m4a,3gp,3g2,mj2 @ 00000193bc227bc0] Unknown cover type: 0x1.
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'E:\sjc.mp4':Metadata:major_brand     : isomminor_version   : 512compatible_brands: isomiso2avc1mp41creation_time   : 2023-04-25T17:47:04.000000ZHw              : 1bitrate         : 16000000maxrate         : 0te_is_reencode  : 1encoder         : Lavf58.76.100Duration: 02:34:43.70, start: 0.000000, bitrate: 12996 kb/sStream #0:0[0x1](und): Video: h264 (Main) (avc1 / 0x31637661), yuv420p(tv, bt709, progressive), 1920x1080 [SAR 1:1 DAR 16:9], 12796 kb/s, 30 fps, 30 tbr, 30 tbn (default)Metadata:creation_time   : 2023-04-25T17:47:04.000000Zhandler_name    : VideoHandlervendor_id       : [0][0][0][0]Stream #0:1[0x2](und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 194 kb/s (default)Metadata:creation_time   : 2023-04-25T17:47:04.000000Zhandler_name    : SoundHandlervendor_id       : [0][0][0][0]
Output #0, mp4, to 'E:\sjc_011210_000125.mp4':Metadata:major_brand     : isomminor_version   : 512compatible_brands: isomiso2avc1mp41te_is_reencode  : 1Hw              : 1bitrate         : 16000000maxrate         : 0encoder         : Lavf59.27.100Stream #0:0(und): Video: h264 (Main) (avc1 / 0x31637661), yuv420p(tv, bt709, progressive), 1920x1080 [SAR 1:1 DAR 16:9], q=2-31, 12796 kb/s, 30 fps, 30 tbr, 15360 tbn (default)Metadata:creation_time   : 2023-04-25T17:47:04.000000Zhandler_name    : VideoHandlervendor_id       : [0][0][0][0]Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 194 kb/s (default)Metadata:creation_time   : 2023-04-25T17:47:04.000000Zhandler_name    : SoundHandlervendor_id       : [0][0][0][0]
Stream mapping:Stream #0:0 -> #0:0 (copy)Stream #0:1 -> #0:1 (copy)
Press [q] to stop, [?] for help
frame=    0 fps=0.0 q=-1.0 size=       0kB time=00:00:00.00 bitrate=N/A speed=   0x    
frame= 1284 fps=0.0 q=-1.0 size=   65792kB time=00:00:43.76 bitrate=12314.9kbits/s speed=87.5x    
frame= 2551 fps=0.0 q=-1.0 Lsize=  130852kB time=00:01:25.00 bitrate=12610.4kbits/s speed=85.4x    
video:128759kB audio:2002kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.070083%
shell执行结束!
Disconnected from the target VM, address: '127.0.0.1:49712', transport: 'socket'Process finished with exit code 0

这样就实现了用Java程序进行视频裁剪的操作,有兴趣的小伙伴可以试一试!

这篇关于Java程序处理视频裁剪(快速处理大文件)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/324797

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

流媒体平台/视频监控/安防视频汇聚EasyCVR播放暂停后视频画面黑屏是什么原因?

视频智能分析/视频监控/安防监控综合管理系统EasyCVR视频汇聚融合平台,是TSINGSEE青犀视频垂直深耕音视频流媒体技术、AI智能技术领域的杰出成果。该平台以其强大的视频处理、汇聚与融合能力,在构建全栈视频监控系统中展现出了独特的优势。视频监控管理系统EasyCVR平台内置了强大的视频解码、转码、压缩等技术,能够处理多种视频流格式,并以多种格式(RTMP、RTSP、HTTP-FLV、WebS

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory