Java使用FFmpeg对视频文件打标记

编程入门 行业动态 更新时间:2024-10-23 16:18:58

Java使用FFmpeg对<a href=https://www.elefans.com/category/jswz/34/1762933.html style=视频文件打标记"/>

Java使用FFmpeg对视频文件打标记

免安装 FFmpeg

<dependency><groupId>ws.schild</groupId><artifactId>jave-all-deps</artifactId><version>3.0.1</version><exclusions><!--  排除windows 32位系统      --><exclusion><groupId>ws.schild</groupId><artifactId>jave-nativebin-win32</artifactId></exclusion><!--  排除linux 32位系统      --><exclusion><groupId>ws.schild</groupId><artifactId>jave-nativebin-linux32</artifactId></exclusion><!-- 排除Mac系统--><exclusion><groupId>ws.schild</groupId><artifactId>jave-nativebin-osx64</artifactId></exclusion></exclusions>
</dependency>
import org.apachemons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ws.schild.jave.Encoder;
import ws.schild.jave.EncoderException;
import ws.schild.jave.MultimediaObject;
import ws.schild.jave.encode.AudioAttributes;
import ws.schild.jave.encode.EncodingAttributes;
import ws.schild.jave.encode.VideoAttributes;
import ws.schild.jave.info.MultimediaInfo;
import ws.schild.jave.process.ProcessWrapper;
import ws.schild.jave.process.ffmpeg.DefaultFFMPEGLocator;import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.URL;
import java.util.List;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;/*** @author Mr.superbeyone* @project * @className FfmpegUtil* @description* @date 2023-10-19 13:37**/
public class FfmpegUtil {private static Logger logger = LoggerFactory.getLogger(FfmpegUtil.class);/*** 通过本地路径获取多媒体文件信息(宽,高,时长,编码等)** @param localPath 本地路径* @return MultimediaInfo 对象,包含 (宽,高,时长,编码等)* @throws EncoderException*/public static MultimediaInfo getMultimediaInfo(String localPath) {MultimediaInfo multimediaInfo = null;try {multimediaInfo = new MultimediaObject(new File(localPath)).getInfo();} catch (EncoderException e) {System.out.println("获取多媒体文件信息异常");e.printStackTrace();}return multimediaInfo;}/*** 通过URL获取多媒体文件信息** @param url 网络url* @return MultimediaInfo 对象,包含 (宽,高,时长,编码等)* @throws EncoderException*/public static MultimediaInfo getMultimediaInfoFromUrl(String url) {MultimediaInfo multimediaInfo = null;try {multimediaInfo = new MultimediaObject(new URL(url)).getInfo();} catch (Exception e) {System.out.println("获取多媒体文件信息异常");e.printStackTrace();}return multimediaInfo;}private static final int SAMPLING_RATE = 16000;private static final int SINGLE_CHANNEL = 1;/*** 音频格式化为wav,并设置单声道和采样率** @param url        需要转格式的音频* @param targetPath 格式化后要保存的目标路径*/public static boolean formatAudio(String url, String targetPath) {File target = new File(targetPath);MultimediaObject multimediaObject;try {// 若是本地文件: multimediaObject = new MultimediaObject(new File("你的本地路径"));multimediaObject = new MultimediaObject(new URL(url));// 音频参数//  此处按需自定义音频参数AudioAttributes audio = new AudioAttributes();// 采样率audio.setSamplingRate(SAMPLING_RATE);// 单声道audio.setChannels(SINGLE_CHANNEL);Encoder encoder = new Encoder();EncodingAttributes attrs = new EncodingAttributes();// 输出格式attrs.setOutputFormat("wav");attrs.setAudioAttributes(audio);encoder.encode(multimediaObject, target, attrs);return true;} catch (Exception e) {System.out.println("格式化音频异常");return false;}}/*** 视频格式化为mp4** @param url* @param targetPath* @return*/public static boolean formatToMp4(String url, String targetPath) {File target = new File(targetPath);MultimediaObject multimediaObject;try {// 若是本地文件: multimediaObject = new MultimediaObject(new File("你的本地路径"));multimediaObject = new MultimediaObject(new URL(url));EncodingAttributes attributes = new EncodingAttributes();// 设置视频的音频参数AudioAttributes audioAttributes = new AudioAttributes();attributes.setAudioAttributes(audioAttributes);// 设置视频的视频参数VideoAttributes videoAttributes = new VideoAttributes();// 设置帧率videoAttributes.setFrameRate(25);attributes.setVideoAttributes(videoAttributes);// 设置输出格式attributes.setOutputFormat("mp4");Encoder encoder = new Encoder();encoder.encode(multimediaObject, target, attributes);return true;} catch (Exception e) {System.out.println("格式化视频异常");e.printStackTrace();return false;}}/*** 获取视频缩略图 获取视频第0秒的第一帧图片** <p>执行的ffmpeg 命令为: ffmpeg -i 你的视频文件路径 -ss 指定的秒数 生成文件的全路径地址** @param localPath  本地路径* @param targetPath 存放的目标路径* @return*/public static boolean getTargetThumbnail(String localPath, String targetPath) {// 该方法基本可作为执行ffmpeg命令的模板方法,之后的几个方法与此类似try {ProcessWrapper ffmpeg = new DefaultFFMPEGLocator().createExecutor();ffmpeg.addArgument("-i");ffmpeg.addArgument(localPath);ffmpeg.addArgument("-ss");// 此处可自定义视频的秒数ffmpeg.addArgument("10");ffmpeg.addArgument(targetPath);ffmpeg.execute();try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {blockFfmpeg(br);}} catch (IOException e) {System.out.println("获取视频缩略图失败");e.printStackTrace();return false;}return true;}/*** 等待命令执行成功,退出** @param br* @throws IOException*/private static void blockFfmpeg(BufferedReader br) throws IOException {String line;// 该方法阻塞线程,直至合成成功while ((line = br.readLine()) != null) {doNothing(line);}}/*** 打印日志** @param line*/private static void doNothing(String line) {// 正式使用时注释掉此行,仅用于观察日志
//        System.out.println(line);}/*** 视频增加字幕** @param originVideoPath 原视频地址* @param targetVideoPath 目标视频地址* @param srtPath         固定格式的srt文件地址或存储位置,字母文件名: xxx.srt,样例看博客* @return* @throws Exception*/public static boolean addSubtitle(String originVideoPath, String srtPath, String targetVideoPath) {try {ProcessWrapper ffmpeg = new DefaultFFMPEGLocator().createExecutor();ffmpeg.addArgument("-i");ffmpeg.addArgument(originVideoPath);ffmpeg.addArgument("-i");ffmpeg.addArgument(srtPath);ffmpeg.addArgument("-c");ffmpeg.addArgument("copy");ffmpeg.addArgument(targetVideoPath);ffmpeg.execute();try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {blockFfmpeg(br);}} catch (IOException e) {System.out.println("字幕增加失败");e.printStackTrace();}return true;}/*** 常用命令** @return*/public static void cmd() {// FIXME: 2023/1/31  还有很多类似命令 不再一一列举 ,附上命令,具体写法参考 getTargetThumbnail或addSubtitle方法// FIXME: 2023/1/31 ffmpeg命令网上搜索即可// 剪切视频// ffmpeg -ss 00:00:00 -t 00:00:30 -i test.mp4 -vcodec copy -acodec copy output.mp4// * -ss 指定从什么时间开始// * -t 指定需要截取多长时间// * -i 指定输入文件// ffmpeg -ss 10 -t 15 -accurate_seek -i test.mp4 -codec copy cut.mp4// ffmpeg -ss 10 -t 15 -accurate_seek -i test.mp4 -codec copy -avoid_negative_ts 1 cut.mp4// 拼接MP4// 第一种方法:// ffmpeg -i "concat:1.mp4|2.mp4|3.mp4" -codec copy out_mp4.mp4// 1.mp4 第一个视频文件的全路径// 2.mp4 第二个视频文件的全路径// 提取视频中的音频// ffmpeg -i input.mp4 -acodec copy -vn output.mp3// -vn: 去掉视频;-acodec: 音频选项, 一般后面加copy表示拷贝// 音视频合成// ffmpeg -y –i input.mp4 –i input.mp3 –vcodec copy –acodec copy output.mp4// -y 覆盖输出文件// 剪切视频//  ffmpeg -ss 0:1:30 -t 0:0:20 -i input.mp4 -vcodec copy -acodec copy output.mp4// -ss 开始时间; -t 持续时间// 视频截图//  ffmpeg –i test.mp4 –f image2 -t 0.001 -s 320x240 image-%3d.jpg// -s 设置分辨率; -f 强迫采用格式fmt;// 视频分解为图片//   ffmpeg –i test.mp4 –r 1 –f image2 image-%3d.jpg// -r 指定截屏频率// 将图片合成视频//  ffmpeg -f image2 -i image%d.jpg output.mp4// 视频拼接//  ffmpeg -f concat -i filelist.txt -c copy output.mp4// 将视频转为gif//    ffmpeg -i input.mp4 -ss 0:0:30 -t 10 -s 320x240 -pix_fmt rgb24 output.gif// -pix_fmt 指定编码// 视频添加水印//  ffmpeg -i input.mp4 -i logo.jpg// -filter_complex[0:v][1:v]overlay=main_w-overlay_w-10:main_h-overlay_h-10[out] -map [out] -map// 0:a -codec:a copy output.mp4// main_w-overlay_w-10 视频的宽度-水印的宽度-水印边距;}//=========================================================================================//private static final int WIDTH = 50, HEIGHT = 50;public static String generateTagVideo(String srcVideoPath) {long start = System.currentTimeMillis();String targetVideo = generateTagVideo(srcVideoPath, null);logger.info("generateTagVideo 耗时 {}", (System.currentTimeMillis() - start));logger.info("generateTagVideo srcVideo {} targetVideo {}", srcVideoPath, targetVideo);return targetVideo;}public static String generateTagVideo(String srcVideoPath, String targetVideoPath) {long start = System.currentTimeMillis();//视频文件路径File srcVideoFile = new File(srcVideoPath);if (!srcVideoFile.exists() || !srcVideoFile.isFile()) {logger.info("generateTagVideo srcVideoPath is not video file {}", srcVideoPath);return "";}//图片文件、视频保存跟路径File genRoot = new File(StringUtils.isBlank(targetVideoPath) ? srcVideoFile.getParentFile().getAbsolutePath() : targetVideoPath,StringUtils.replace(srcVideoFile.getName(), ".", "_") + "_gen");String targetFileName = StringUtils.substringBeforeLast(srcVideoFile.getName(), ".") + "_gen" + ".mp4";//判断是否存在已生成的视频文件if (genRoot.exists() && genRoot.isDirectory()) {Optional<File> optionalFile = Arrays.stream(Objects.requireNonNull(genRoot.listFiles())).filter(f -> StringUtils.equals(f.getName(), targetFileName)).findFirst();if (optionalFile.isPresent()) {return optionalFile.get().getAbsolutePath();}}//图片路径File imgDir = new File(genRoot, "img");imgDir.mkdirs();//视频文件拆分成图片splitVideo2Img(srcVideoPath, imgDir.getAbsolutePath());//获取要RPC调用的文件List<File> imgList = getImgList(imgDir);//TODO 跟进图片,比对后,获取图片及坐标信息int x = 1000, y = 500;//TODO 获取需要标注的图片文件集合,及标注坐标// imgList = new ArrayList<>();//替换文件replaceImgFile(imgList, x, y, WIDTH, HEIGHT);//合并图片文件为视频String targetPath = mergeImg2Video(genRoot, targetFileName, imgDir.getAbsolutePath());logger.info("generateTagVideo 耗时:{},src:\t {},\t target:\t{}",(System.currentTimeMillis() - start), srcVideoPath, targetPath);//删除图片文件//deleteFile(imgDir);return targetPath;}private static String mergeImg2Video(File videoPath, String targetFileName, String imgPath) {try (ProcessWrapper ffmpeg = new DefaultFFMPEGLocator().createExecutor()) {ffmpeg.addArgument("-f");ffmpeg.addArgument("image2");ffmpeg.addArgument("-i");ffmpeg.addArgument(imgPath + File.separator + "image_%6d.jpg");String video = videoPath + File.separator + targetFileName;ffmpeg.addArgument(video);ffmpeg.execute();hold(ffmpeg);return video;} catch (Exception e) {logger.error("mergeImg2Video error imgPath {}", imgPath, e);}return "";}public static String splitVideo2Img(String path, String target) {try (ProcessWrapper ffmpeg = new DefaultFFMPEGLocator().createExecutor()) {long start = System.currentTimeMillis();ffmpeg.addArgument("-i");ffmpeg.addArgument(path);ffmpeg.addArgument("-r");ffmpeg.addArgument("10"); //提取图片频率,视频默认一秒25帧,数字越小,视频播放越快ffmpeg.addArgument("-f");ffmpeg.addArgument("image2");ffmpeg.addArgument(target + File.separator + "image_%6d.jpg");ffmpeg.execute();ffmpeg.getProcessExitCode();logger.info("split2Img 耗时:{}", (System.currentTimeMillis() - start));} catch (Exception e) {logger.error("splitVideo2Img error ", e);}return target;}private static void hold(ProcessWrapper ffmpeg) throws Exception {try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {blockFfmpeg(br);}}private static void replaceImgFile(List<File> imgList, int x, int y, int width, int height) {long start = System.currentTimeMillis();logger.info("替换文件开始 {}", start);imgList.stream().map(file ->CompletableFuture.runAsync(() ->drawImg(file, x, y, width, height))).collect(Collectors.toList()).stream().map(CompletableFuture::join).collect(Collectors.toList());logger.info("替换文件结束 ,共耗时:{}", (System.currentTimeMillis() - start));}/*** 获取根目录下的所有文件** @param imgRoot 根目录* @return 所有文件*/private static List<File> getImgList(File imgRoot) {File[] files = imgRoot.listFiles();return Arrays.stream(files).collect(Collectors.toList());}/*** 删除文件** @param file 要删除的文件*/private static void deleteFile(File file) {//删除图片文件Arrays.stream(Objects.requireNonNull(file.listFiles())).map(f -> CompletableFuture.runAsync(f::delete)).collect(Collectors.toList()).stream().map(CompletableFuture::join).collect(Collectors.toList());file.delete();}/*** 对图片文件 画矩形标注** @param file   图片文件* @param x      位置x* @param y      位置y* @param width  标注宽度* @param height 标注高度*/public static void drawImg(File file, int x, int y, int width, int height) {try  {BufferedImage image = ImageIO.read(file);Graphics graphics = image.getGraphics();graphics.setColor(Color.RED);graphics.drawRect(x, y, width, height);FileOutputStream outputStream = new FileOutputStream(file);ImageIO.write(image, "jpeg", outputStream);outputStream.close();} catch (Exception e) {logger.error("drawImg error ", e);}}public static void main(String[] args) {String path = "D:\\WorkSpace\\video\\src\\b.mp4";String result = generateTagVideo(path);System.out.println(result);}}

参考

更多推荐

Java使用FFmpeg对视频文件打标记

本文发布于:2023-12-07 01:41:02,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1669687.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:视频文件   标记   Java   FFmpeg

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!