视频转MP4执行命令
ffmpeg -i C:\Users\Administrator\Desktop\111\疾速追杀.mkv -y -metadata:s:v rotate=0 -y -c:v libx264 -c:a mp3 -strict -1 C:\Users\Administrator\Desktop\111\疾速追杀.mp4 -metadata:s:v rotate=0 -cpu-used 1
说明:
-metadata:s:v rotate=0 是用来取消视频元数据翻转的,部分手机拍出来的视频会有翻转现象产生,这个参数是用来重置视频为正常方向的
-strict -1 这个参数表示使用严格级别进行编码,编码器会完全遵循编码规范。使用这个参数是因为某次在解析mov格式视频时命令执行失败,加上此参数问题解决。
MP4转M3U8切片执行命令
ffmpeg -i C:\Users\Administrator\Desktop\111\疾速追杀.mp4 -codec:v copy -codec:a copy -map 0 -f ssegment -segment_format mpegts -segment_list C:\Users\Administrator\Desktop\aaa\疾速追杀.m3u8 -segment_time 10 C:\Users\Administrator\Desktop\aaa\疾速追杀%05d.ts -cpu-used 1
注意:其他格式视频也可以直接转M3U8,但是MKV格式视频貌似有一部分会失败,失败原因可能是因为有些MKV音频多音轨相关,需要先转成MP4。
工具类代码
/**
* @author: wxw
* @time: 2023/2/7 13:13
* @description:
*/
package cn.piesat.space.obs.utils;
import cn.piesat.space.obs.constants.ObsConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.io.IOUtils;
import org.springframework.http.MediaType;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import ws.schild.jave.process.ProcessWrapper;
import ws.schild.jave.process.ffmpeg.DefaultFFMPEGLocator;
import java.io.*;
import java.util.Calendar;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author ruibo.duan, <ruibo.duan@mobvoi.com>
* @since 2021/7/22
*/
@Slf4j
public class FfmpegUtil {
private FfmpegUtil() {
}
private static void blockFfmpeg(BufferedReader br) throws IOException {
String line;
// 该方法阻塞线程,直至合成成功
while ((line = br.readLine()) != null) {
doNothing(line);
}
}
private static void doNothing(String line) {
// FIXME: 2023/1/31 正式使用时注释掉此行,仅用于观察日志
System.out.println(line);
}
// public static void delFile(){
// String targetPath = ObsConstant.TARGET_MP4_PATH;
// File srcDir = new File(targetPath);
// if (!srcDir.exists()) {
// return;
// }
// File[] files = srcDir.listFiles();
// if (files == null || files.length <= 0) {
// return;
// }
// Date today = new Date();
// for (int i = 0; i < files.length; i++) {
// try {
// File ff = files[i];
// long time=ff.lastModified();
// Calendar cal=Calendar.getInstance();
// cal.setTimeInMillis(time);
// Date lastModified = cal.getTime();
// long days = getDistDates(today, lastModified);
// if(days>0){
// if (ff.isDirectory()){
// File[] ffFiles = ff.listFiles();
// if (ffFiles == null || ffFiles.length <= 0) {
// continue;
// }
// for (int j = 0; j < ffFiles.length; j++) {
// if (ffFi les[j].isFile()){
// ffFiles[j].delete();
// }
// }
// }
// ff.delete();
// }
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
// }
public static long getDistDates(Date startDate, Date endDate) {
long totalDate = 0;
Calendar calendar = Calendar.getInstance();
calendar.setTime(startDate);
long timestart = calendar.getTimeInMillis();
calendar.setTime(endDate);
long timeend = calendar.getTimeInMillis();
totalDate = Math.abs((timeend - timestart)) / (1000 * 60 * 60 * 24);
return totalDate;
}
/**
* 转MP4
*
* @return
*/
public static String processAnyVideo2Mp4(String inputFilePath, String targetPath) {
try {
long currentMillis = System.currentTimeMillis();
ProcessWrapper ffmpeg = new DefaultFFMPEGLocator().createExecutor();
ffmpeg.addArgument("-i");
ffmpeg.addArgument(inputFilePath);
ffmpeg.addArgument("-metadata:s:v");
ffmpeg.addArgument("rotate=0");
ffmpeg.addArgument("-y");
ffmpeg.addArgument("-c:v");
ffmpeg.addArgument("libx264");
ffmpeg.addArgument("-c:a");
ffmpeg.addArgument("mp3");
ffmpeg.addArgument(targetPath);
ffmpeg.addArgument("-cpu-used");
ffmpeg.addArgument("1");
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
return targetPath;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 获取视频时长
*/
public static long getVideoDuration(String inputFilePath) throws Exception {
long videoDuration = 0;
ProcessWrapper process = new DefaultFFMPEGLocator().createExecutor();
process.addArgument("-i");
process.addArgument(inputFilePath);
process.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
String line;
while ((line = br.readLine()) != null) {
if (line.contains("Duration:")) {
videoDuration = cutVideoDuration(line);
break;
}
}
}
return videoDuration;
}
/**
* 截取ffmpeg视频信息中的视频时长
*/
public static long cutVideoDuration(String line) throws Exception {
Pattern pattern = Pattern.compile(ObsConstant.FFMPEG_DURATION_REGEX);
Matcher matcher = pattern.matcher(line);
long duration = 0L;
if (matcher.find()) {
// 计算时长
long hour = Long.parseLong(matcher.group(1));//group1为第一个括号内的内容
long minute = Long.parseLong(matcher.group(2));//group2为第二个括号内容
long second = Long.parseLong(matcher.group(3));//group3为第二个括号内容
long millisecond = Long.parseLong(matcher.group(4));//group4为第二个括号内容
duration = hour * 3600000 + minute * 60000 + second * 1000 + millisecond;
} else {
System.out.println("NOT FOUND");
}
return duration;
}
public static void videoToM3u8(String inputFilePath, String m3u8OutputPath, String tsOutputPath) throws Exception {
ProcessWrapper process = new DefaultFFMPEGLocator().createExecutor();
process.addArgument("-i");
process.addArgument(inputFilePath);
process.addArgument("-codec:v");
process.addArgument("copy");
process.addArgument("-codec:a");
process.addArgument("copy");
process.addArgument("-map");
process.addArgument("0");
process.addArgument("-f");
process.addArgument("ssegment");
process.addArgument("-segment_format");
process.addArgument("mpegts");
process.addArgument("-segment_list");
process.addArgument(m3u8OutputPath);
process.addArgument("-segment_time");
process.addArgument("10");
process.addArgument("-cpu-used");
process.addArgument("1");
process.addArgument(tsOutputPath);
process.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
blockFfmpeg(br);
}
}
public static void videoScreenshot(String inputFilePath, String screenshotOutputPath) throws Exception {
try {
ProcessWrapper command = new DefaultFFMPEGLocator().createExecutor();
// 创建一个List集合来保存从视频中截取图片的命令
command.addArgument("-i");
command.addArgument(inputFilePath); // 同上(指定的文件即可以是转换为flv格式之前的文件,也可以是转换的flv文件)
command.addArgument("-y");
command.addArgument("-f");
command.addArgument("image2");
command.addArgument("-ss"); // 添加参数"-ss",该参数指定截取的起始时间
command.addArgument("1"); // 添加起始时间为第1秒
command.addArgument("-t"); // 添加参数"-t",该参数指定持续时间
command.addArgument("0.001"); // 添加持续时间为1毫秒
command.addArgument("-cpu-used");
command.addArgument("1");
// command.addArgument("-s"); // 添加参数"-s",该参数指定截取的图片大小
// command.addArgument("0*0"); // 添加截取的图片大小为350*240
command.addArgument(screenshotOutputPath); // 添加截取的图片的保存路径
command.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(command.getErrorStream()))) {
blockFfmpeg(br);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static MultipartFile File2MultipartFile(File file) {
FileItem item = new DiskFileItemFactory().createItem("file", MediaType.MULTIPART_FORM_DATA_VALUE, true, file.getName());
try (InputStream input = new FileInputStream(file); OutputStream os = item.getOutputStream()) {
IOUtils.copy(input, os);
} catch (Exception e) {
throw new IllegalArgumentException("Invalid file: " + e, e);
}
return new CommonsMultipartFile(item);
}
/**
* 常用命令
*
* @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 视频的宽度-水印的宽度-水印边距;
}
}