package com.ruoyi.svm.utils;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.ruoyi.svm.dto.WordAndCoverStyleDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.bytedeco.ffmpeg.ffmpeg;
import org.bytedeco.javacpp.Loader;
import org.springframework.stereotype.Component;
import ws.schild.jave.EncoderException;
import ws.schild.jave.MultimediaObject;
import ws.schild.jave.info.MultimediaInfo;
import ws.schild.jave.process.ProcessWrapper;
import ws.schild.jave.process.ffmpeg.DefaultFFMPEGLocator;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.net.URL;
import java.text.MessageFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Slf4j
@Component
public class MyFFmpegUtil {
/**
* 等待命令执行成功,退出
*
* @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) {
// log.info(line);
}
/**
* 音频格式转换
*
* @param sourcePath 源
* @param targetPath 目标
* @throws Exception
*/
public static void formatAudio(String sourcePath, String targetPath, String ss, String t) throws Exception {
ProcessWrapper ffmpeg = null;
try {
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
//下面两行都是为了可以直接接受url作为输入
ffmpeg.addArgument("-protocol_whitelist");
ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
if (StringUtils.isNotEmpty(ss) || StringUtils.isNotEmpty(t)) {
ffmpeg.addArgument("-ss");
ffmpeg.addArgument(ss);
ffmpeg.addArgument("-to");
ffmpeg.addArgument(t);
}
ffmpeg.addArgument("-i");
ffmpeg.addArgument(sourcePath);
ffmpeg.addArgument(targetPath);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
} catch (IOException e) {
throw new Exception("转换格式成功失败");
}finally{
if (null!=ffmpeg){
ffmpeg.close();
}
}
}
/**
* 视频格式转换
*
* @param sourcePath 源视频
* @param targetPath 目标视频
* @throws Exception
*/
public static void formatVideo(String sourcePath, String targetPath, String ss, String t) throws Exception {
ProcessWrapper ffmpeg=null;
try {
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
//下面两行都是为了可以直接接受url作为输入
ffmpeg.addArgument("-protocol_whitelist");
ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
if (StringUtils.isNotEmpty(ss) || StringUtils.isNotEmpty(t)) {
ffmpeg.addArgument("-ss");
ffmpeg.addArgument(ss);
ffmpeg.addArgument("-to");
ffmpeg.addArgument(t);
}
ffmpeg.addArgument("-i");
ffmpeg.addArgument(sourcePath);
//直接复制音视频,不经过重新编码(这样比较快)
ffmpeg.addArgument("-codec");
ffmpeg.addArgument("copy");
ffmpeg.addArgument(targetPath);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
} catch (IOException e) {
throw new Exception("转换格式成功失败");
}finally{
if (null!=ffmpeg){
ffmpeg.close();
}
}
}
/**
* 提取视频
*
* @param sourcePath
* @param targetPath
* @throws Exception
*/
public static void extractVideo(String sourcePath, String targetPath, String ss, String t) throws Exception {
ProcessWrapper ffmpeg=null;
try {
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
//下面两行都是为了可以直接接受url作为输入
ffmpeg.addArgument("-protocol_whitelist");
ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
if (StringUtils.isNotEmpty(ss) || StringUtils.isNotEmpty(t)) {
ffmpeg.addArgument("-ss");
ffmpeg.addArgument(ss);
ffmpeg.addArgument("-to");
ffmpeg.addArgument(t);
}
ffmpeg.addArgument("-i");
ffmpeg.addArgument(sourcePath);
//去掉音频
ffmpeg.addArgument("-an");
//直接复制视频,不经过重新编码(这样比较快)
ffmpeg.addArgument("-vcodec");
ffmpeg.addArgument("copy");
ffmpeg.addArgument(targetPath);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
} catch (IOException e) {
throw new Exception("提取视频失败");
}
finally{
if (null!=ffmpeg){
ffmpeg.close();
}
}
}
/**
* 提取音频
* 音频需要对应视频保存的音频格式才行
*
* @param sourcePath
* @param targetPath
* @throws Exception
*/
public static void extractAudio(String sourcePath, String targetPath, String vol, String ss, String t) throws Exception {
ProcessWrapper ffmpeg=null;
try {
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
//下面两行都是为了可以直接接受url作为输入
ffmpeg.addArgument("-protocol_whitelist");
ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
if (StringUtils.isNotEmpty(ss) || StringUtils.isNotEmpty(t)) {
ffmpeg.addArgument("-ss");
ffmpeg.addArgument(ss);
ffmpeg.addArgument("-to");
ffmpeg.addArgument(t);
}
ffmpeg.addArgument("-i");
ffmpeg.addArgument(sourcePath);
//音量设置
if (null != vol) {
String s1 = "volume = " + vol;
ffmpeg.addArgument("-af");
ffmpeg.addArgument(s1);
}
//去掉视频
ffmpeg.addArgument("-vn");
ffmpeg.addArgument(targetPath);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
} catch (IOException e) {
throw new Exception("提取音频失败");
}
finally{
if (null!=ffmpeg){
ffmpeg.close();
}
}
}
/**
* @return void
* @Author liufangli
* @Description 修改视频分辨率
* @Date 9:02 2022/7/21
* @Param [sourcePath, targetPath, height, width]
**/
public static void updateDpiAudio(String pathHead, String sourcePath, String targetPath) throws Exception {
String tmp = pathHead + IdWorker.getId() + ".mp4";
ProcessWrapper ffmpeg=null;
try {
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
//下面两行都是为了可以直接接受url作为输入
ffmpeg.addArgument("-protocol_whitelist");
ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
ffmpeg.addArgument("-i");
ffmpeg.addArgument(sourcePath);
ffmpeg.addArgument("-vf");
//视频宽:720 高:等比例自适应 背景:720*1280 视频居中背景
ffmpeg.addArgument("scale=720:-1,pad=720:1280:0:(1280-ih)/2:black");
ffmpeg.addArgument(tmp);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
ffmpeg.close();
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
ffmpeg.addArgument("-i");
ffmpeg.addArgument(tmp);
ffmpeg.addArgument("-vf");
//设置sar 后期拼接视频才不会报错
ffmpeg.addArgument("setsar=1");
ffmpeg.addArgument("-f");
ffmpeg.addArgument("mp4");
//统一帧数25 保证后期合并视频不出错
ffmpeg.addArgument("-r");
ffmpeg.addArgument("25");
ffmpeg.addArgument(targetPath);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
} catch (IOException e) {
throw new Exception("转化视频分辨率失败");
} finally {
File file = new File(tmp);
if (file.exists()) {
file.delete();
}
if (null!=ffmpeg){
ffmpeg.close();
}
}
}
/**
* 随机深色背景
**/
public static void randomDarkBack(String pathHead, String sourcePath, String targetPath, String color) throws Exception {
String tmp = pathHead + IdWorker.getId() + ".mp4";
ProcessWrapper ffmpeg=null;
try {
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
//下面两行都是为了可以直接接受url作为输入
ffmpeg.addArgument("-protocol_whitelist");
ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
ffmpeg.addArgument("-i");
ffmpeg.addArgument(sourcePath);
//设置sar 视频宽:720 高:等比例自适应 背景:720*1280 视频居中背景
ffmpeg.addArgument("-vf");
ffmpeg.addArgument("setsar=1,scale=720:-1,pad=720:1280:0:(1280-ih)/2:" + color);
ffmpeg.addArgument(tmp);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
ffmpeg.close();
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
ffmpeg.addArgument("-i");
ffmpeg.addArgument(tmp);
//统一帧数25 保证后期合并视频不出错
ffmpeg.addArgument("-r");
ffmpeg.addArgument("25");
ffmpeg.addArgument("-f");
ffmpeg.addArgument("mp4");
//去掉音频
ffmpeg.addArgument("-an");
ffmpeg.addArgument(targetPath);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
} catch (IOException e) {
throw new Exception("转化视频分辨率失败");
} finally {
File file = new File(tmp);
if (file.exists()) {
file.delete();
}
if (null!=ffmpeg){
ffmpeg.close();
}
}
}
/**
* 视频加声音
* 必须是纯视频和纯音频
*
* @param videoPath 视频
* @param megerAudioPath 音频
* @param videoTargetPath 目标地址
* @throws Exception
*/
public static void mergeVideoAndAudio(
String videoPath, String megerAudioPath, String videoTargetPath) throws Exception {
ProcessWrapper ffmpeg=null;
try {
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
//下面两行都是为了可以直接接受url作为输入
ffmpeg.addArgument("-protocol_whitelist");
ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
ffmpeg.addArgument("-i");
ffmpeg.addArgument(videoPath);
ffmpeg.addArgument("-i");
ffmpeg.addArgument(megerAudioPath);
//合成对象长度以最短的一方为准
ffmpeg.addArgument("-shortest");
ffmpeg.addArgument("-c:v");
ffmpeg.addArgument("libx264");
ffmpeg.addArgument("-strict");
ffmpeg.addArgument("-2");
ffmpeg.addArgument(videoTargetPath);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
} catch (IOException e) {
throw new Exception("音视频拼接");
}
finally{
if (null!=ffmpeg){
ffmpeg.close();
}
}
}
/**
* 合并多个视频(有声)
*
* @throws Exception
*/
// public static void mergeVideoAndAudio(List<String> paths, String targetPath) throws Exception {
// ProcessWrapper ffmpeg=null;
// try {
// ffmpeg = new DefaultFFMPEGLocator().createExecutor();
// ffmpeg.addArgument("-protocol_whitelist");
// ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
// String s0 = "\"";
// String s1 = "concat=n=" + paths.size() + ":v=1:a=1 [v] [a]\" -map \"[v]\" -map \"[a]\"";
// for (Integer i = 0; i < paths.size(); i++) {
// s0 += "[s:v] [s:a] ".replaceAll("s", i.toString());
// ffmpeg.addArgument("-i");
// ffmpeg.addArgument(paths.get(i));
// }
// String s = s0 + s1;
// ffmpeg.addArgument("-filter_complex");
//
// ffmpeg.addArgument(s);
//
// ffmpeg.addArgument(targetPath);
// ffmpeg.execute();
// try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
// blockFfmpeg(br);
// }
log.debug("合并视频成功={}", targetPath);
// } catch (IOException e) {
// throw new Exception("合并视频失败");
// }finally{
// if (null!=ffmpeg){
// ffmpeg.close();
// }
// }
// }
/**
* 合并多个视频(无声)
*
* @throws Exception
*/
public static void mergeVideo(List<String> paths, String targetPath, String t) throws Exception {
ProcessWrapper ffmpeg=null;
try {
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
ffmpeg.addArgument("-protocol_whitelist");
ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
String s0 = "\"";
String s1 = "concat=n=" + paths.size() + ":v=1 [v]\" -map \"[v]\"";
String s2 = "";
for (Integer i = 0; i < paths.size(); i++) {
s0 += "[@:v] scale=720:1280:force_original_aspect_ratio=1[v@];".replaceAll("@", i.toString());
s2 += "[v@]".replaceAll("@", i.toString());
ffmpeg.addArgument("-i");
ffmpeg.addArgument(paths.get(i));
}
String s = s0 + s2 + s1;
ffmpeg.addArgument("-filter_complex");
ffmpeg.addArgument(s);
if (null != t) {
ffmpeg.addArgument("-t");
ffmpeg.addArgument(t);
}
ffmpeg.addArgument(targetPath);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
} catch (IOException e) {
throw new Exception("合并视频失败");
}finally{
if (null!=ffmpeg){
ffmpeg.close();
}
}
}
/***
* 合成视频带有过渡
* @param paths 要合成的视频
* @param targetPath 输出路径
* @param offset 要合成的视频时间长度
* @param duration 过渡长度 要合成视频长度必须要是过渡长度的两倍或以上
* @param t 最终视频时长,传null 就不做截取
* @param fade 每次的过渡效果
* @throws Exception
*/
public static void mergeVideo1(List<String> paths, String targetPath, List<String> offset, Integer duration, String t, List<String> fade) throws Exception {
Random random = new Random();
ProcessWrapper ffmpeg=null;
try {
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
ffmpeg.addArgument("-protocol_whitelist");
ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
String s1 = "[0]";
String s2 = "";
for (Integer i = 0; i < paths.size(); i++) {
//非第一个
if (i != 0 ) {
String tmp = ("[s@]").replaceAll("@", i.toString());
s2 += s1 + ("[@]xfade=transition=" + fade.get(random.nextInt(fade.size())) + ":duration=" + duration + ":offset=" + offset.get(i - 1)).replaceAll("@", i.toString());
//非最后一个 分号隔开
if (i != paths.size() - 1) {
s2 += tmp + ";";
}
s1 = tmp;
}
ffmpeg.addArgument("-i");
ffmpeg.addArgument(paths.get(i));
}
String s = "\"" + s2 + "\"";
ffmpeg.addArgument("-filter_complex");
ffmpeg.addArgument(s);
if (null != t) {
ffmpeg.addArgument("-t");
ffmpeg.addArgument(t);
}
ffmpeg.addArgument(targetPath);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
} catch (IOException e) {
throw new Exception("合并视频失败");
}finally{
if (null!=ffmpeg){
ffmpeg.close();
}
}
}
/**
* 合成视频带有过渡 场景顺序专用
*
* @throws Exception
*/
public static void mergeVideo2(List<String> paths, String targetPath, List<String> offset, Integer duration, String t, List<String> fade, List<String> scene, List<String> curScene) throws Exception {
Random random = new Random();
ProcessWrapper ffmpeg=null;
try {
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
ffmpeg.addArgument("-protocol_whitelist");
ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
String s1 = "[0]";
String s2 = "";
Integer index=0;
for (Integer i = 0; i < paths.size(); i++) {
//非第一个
if (i != 0 ) {
if (scene.contains(curScene.get(i-1))){
index++;
BigDecimal subtract = new BigDecimal(offset.get(i - 1)).subtract(new BigDecimal(index * duration));
String tmp = ("[s@]").replaceAll("@", i.toString());
s2 += s1 + ("[@]xfade=transition=" + fade.get(random.nextInt(fade.size())) + ":duration=" + duration + ":offset=" + subtract.toString()).replaceAll("@", i.toString());
//非最后一个 分号隔开
if (i != paths.size() - 1) {
s2 += tmp + ";";
}
s1 = tmp;
}
else
{
index++;
BigDecimal subtract = new BigDecimal(offset.get(i - 1)).subtract(new BigDecimal(index * 1));
String tmp = ("[s@]").replaceAll("@", i.toString());
s2 += s1 + ("[@]xfade=transition=" + fade.get(random.nextInt(fade.size())) + ":duration=" + 0 + ":offset=" + subtract.toString()).replaceAll("@", i.toString());
//非最后一个 分号隔开
if (i != paths.size() - 1) {
s2 += tmp + ";";
}
s1 = tmp;
}
}
ffmpeg.addArgument("-i");
ffmpeg.addArgument(paths.get(i));
}
String s = "\"" + s2 + "\"";
if (!"".equals(s2)){
ffmpeg.addArgument("-filter_complex");
ffmpeg.addArgument(s);
}
if (null != t) {
ffmpeg.addArgument("-t");
ffmpeg.addArgument(t);
}
ffmpeg.addArgument(targetPath);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
} catch (IOException e) {
throw new Exception("合并视频失败");
}finally{
if (null!=ffmpeg){
ffmpeg.close();
}
}
}
/**
* 和mergeVideo1是一样的
* @param paths
* @param targetPath
* @param offset
* @param duration
* @param t
* @param fade
* @throws Exception
*/
public static void mergeVideo3(List<String> paths, String targetPath, List<String> offset, Integer duration, String t, List<String> fade) throws Exception {
Random random = new Random();
ProcessWrapper ffmpeg=null;
try {
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
ffmpeg.addArgument("-protocol_whitelist");
ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
String s1 = "[0]";
String s2 = "";
for (Integer i = 0; i < paths.size(); i++) {
//非第一个
if (i != 0 ) {
BigDecimal subtract = new BigDecimal(offset.get(i - 1)).subtract(new BigDecimal(i * duration));
String tmp = ("[s@]").replaceAll("@", i.toString());
s2 += s1 + ("[@]xfade=transition=" + fade.get(random.nextInt(fade.size())) + ":duration=" + duration + ":offset=" +subtract.toString() ).replaceAll("@", i.toString());
//非最后一个 分号隔开
if (i != paths.size() - 1) {
s2 += tmp + ";";
}
s1 = tmp;
}
ffmpeg.addArgument("-i");
ffmpeg.addArgument(paths.get(i));
}
String s = "\"" + s2 + "\"";
ffmpeg.addArgument("-filter_complex");
ffmpeg.addArgument(s);
if (null != t) {
ffmpeg.addArgument("-t");
ffmpeg.addArgument(t);
}
ffmpeg.addArgument(targetPath);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
} catch (IOException e) {
throw new Exception("合并视频失败");
}finally{
if (null!=ffmpeg){
ffmpeg.close();
}
}
}
/**
* 生成的是gif
* @param paths
* @param targetPath
* @param offset
* @param duration
* @param t
* @param fade
* @throws Exception
*/
public static void mergeVideo4(List<String> paths, String targetPath, List<String> offset, Integer duration, String t, List<String> fade) throws Exception {
ProcessWrapper ffmpeg=null;
Random random = new Random();
try {
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
ffmpeg.addArgument("-protocol_whitelist");
ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
String s1 = "[0]";
String s2 = "";
for (Integer i = 0; i < paths.size(); i++) {
//非第一个
if (i != 0 ) {
BigDecimal subtract = new BigDecimal(offset.get(i - 1)).subtract(new BigDecimal(i * duration));
String tmp = ("[s@]").replaceAll("@", i.toString());
s2 += s1 + ("[@]xfade=transition=" + fade.get(random.nextInt(fade.size())) + ":duration=" + duration + ":offset=" +subtract.toString() ).replaceAll("@", i.toString());
//非最后一个 分号隔开
if (i != paths.size() - 1) {
s2 += tmp + ";";
}
s1 = tmp;
}
ffmpeg.addArgument("-i");
ffmpeg.addArgument(paths.get(i));
}
String s = "\"" + s2 + "\"";
ffmpeg.addArgument("-filter_complex");
ffmpeg.addArgument(s);
if (null != t) {
ffmpeg.addArgument("-t");
ffmpeg.addArgument(t);
}
ffmpeg.addArgument(targetPath);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
} catch (IOException e) {
throw new Exception("合并视频失败");
}finally{
if (null!=ffmpeg){
ffmpeg.close();
}
}
}
/**
* 合并多个视频(无声)
*
* @throws Exception
*/
// public static void mergeVideo2(List<String> paths, String targetPath, List<String> offset, Integer duration, String t) throws Exception {
// Random random = new Random();
// List<String> fade = new ArrayList<>();
// fade.add("dissolve");
// fade.add("hblur");
// fade.add("hrslice");
// fade.add("hlslice");
// fade.add("pixelize");
// fade.add("radial");
// fade.add("rectcrop");
// fade.add("slidedown");
// fade.add("slideleft");
// fade.add("slideright");
// fade.add("slideup");
// fade.add("smoothdown");
// fade.add("smoothleft");
// fade.add("smoothright");
// fade.add("smoothup");
// fade.add("squeezeh");
// fade.add("squeezev");
// fade.add("vdslice");
// fade.add("wipebr");
// fade.add("wipebl");
// fade.add("circleclose");
// fade.add("circlecrop");
// fade.add("circleopen");
// try {
// ProcessWrapper ffmpeg = new DefaultFFMPEGLocator().createExecutor();
// ffmpeg.addArgument("-protocol_whitelist");
// ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
// String s1 = "[0]";
// String s2 = "";
// for (Integer i = 0; i < paths.size(); i++) {
// //非第一个
// if (i != 0) {
// String tmp = ("[s@]").replaceAll("@", i.toString());
// s2 += s1 + ("[@]xfade=transition=" + fade.get(random.nextInt(fade.size())) + ":duration=" + duration + ":offset=" + offset.get(i - 1)).replaceAll("@", i.toString());
// //非最后一个 分号隔开
// if (i != paths.size() - 1) {
// s2 += tmp + ";";
// }
// s1 = tmp;
// }
//
// ffmpeg.addArgument("-i");
// ffmpeg.addArgument(paths.get(i));
// }
// String s = "\"" + s2 + "\"";
// ffmpeg.addArgument("-filter_complex");
// ffmpeg.addArgument(s);
// if (null != t) {
// ffmpeg.addArgument("-t");
// ffmpeg.addArgument(t);
// }
// ffmpeg.addArgument(targetPath);
// ffmpeg.execute();
// try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
// blockFfmpeg(br);
// }
// } catch (IOException e) {
// throw new Exception("合并视频失败");
// }
// }
/**
* 循环拼接音频
*
* @throws Exception
*/
public static void mergeAudio(String pathHead,int size, String sourcePath, String targetPath, String vol) throws Exception {
ProcessWrapper ffmpeg =null;
List<File> files=new ArrayList<>();
try {
String tmp=pathHead+IdWorker.getId()+".mp3";
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
//去除静默
ffmpeg.addArgument("-i");
ffmpeg.addArgument(sourcePath);
ffmpeg.addArgument("-c:v");
ffmpeg.addArgument("copy");
ffmpeg.addArgument("-ac");
ffmpeg.addArgument("2");
ffmpeg.addArgument(tmp);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
ffmpeg.close();
File tmpFile=new File(tmp);
files.add(tmpFile);
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
ffmpeg.addArgument("-protocol_whitelist");
ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
ffmpeg.addArgument("-i");
String s = "concat:";
for (int i = 0; i < size; i++) {
if (i == size - 1) {
s += tmp;
} else {
s += tmp + "|";
}
}
ffmpeg.addArgument(s);
String s1 = "volume = " + vol;
ffmpeg.addArgument("-af");
ffmpeg.addArgument(s1);
ffmpeg.addArgument(targetPath);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
} catch (IOException e) {
throw new Exception("合并视频失败");
}finally{
if (CollectionUtils.isNotEmpty(files)){
for (File file:files){
if (null!=file&&file.exists()){
file.delete();
}
}
}
if (null!=ffmpeg){
ffmpeg.close();
}
}
}
/**
* 循环拼接音频
*
* @throws Exception
*/
public static void mergeAudioAndAudio(List<String> paths, String targetPath, String vol) throws Exception {
ProcessWrapper ffmpeg =null;
try {
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
ffmpeg.addArgument("-protocol_whitelist");
ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
ffmpeg.addArgument("-i");
String s = "concat:";
for (int i = 0; i < paths.size(); i++) {
if (i == paths.size() - 1) {
s += paths.get(i);
} else {
s += paths.get(i) + "|";
}
}
ffmpeg.addArgument(s);
String s1 = "volume = " + vol;
ffmpeg.addArgument("-af");
ffmpeg.addArgument(s1);
ffmpeg.addArgument(targetPath);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
} catch (IOException e) {
throw new Exception("合并视频失败");
}finally{
if (null!=ffmpeg){
ffmpeg.close();
}
}
}
// public static void picToVideo(String sourcePath, String width, String height, String time, String targetPath, Integer tran) throws Exception {
ffmpeg -r 25 -loop 1 -i xinjinping.jpg -pix_fmt yuv420p -vcodec libx264 -b:v 600k -r:v 25 -preset medium -crf 30 -s 720x576 -vframes 250 -r 25 -t 10 a.mp4
-r 25 -loop 1 -i ~/IMG_8679.JPG -pix_fmt yuv420p -vcodec libx264 -b:v 600k -r:v 25 -preset medium -crf 30 -s 720x576 -vframes 250 -r 25 -t 10 ~/a.mp4[/mw_shl_code]
//
// try {
// ProcessWrapper ffmpeg = new DefaultFFMPEGLocator().createExecutor();
// ffmpeg.addArgument("-protocol_whitelist");
// ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
//
// ffmpeg.addArgument("-r");
// if (tran == 1) {
// ffmpeg.addArgument("1");
// } else {
// ffmpeg.addArgument("8");
// }
// ffmpeg.addArgument("-f");
// ffmpeg.addArgument("image2");
//
// ffmpeg.addArgument("-s");
// ffmpeg.addArgument(width + "x" + height);
// ffmpeg.addArgument("-pix_fmt");
// ffmpeg.addArgument("yuv420p");
// ffmpeg.addArgument("-loop");
// ffmpeg.addArgument("1");
// ffmpeg.addArgument("-i");
// ffmpeg.addArgument(sourcePath);
// ffmpeg.addArgument("-t");
// ffmpeg.addArgument(time);
// ffmpeg.addArgument("-vcodec");
// ffmpeg.addArgument("libx264");
// ffmpeg.addArgument(targetPath);
// ffmpeg.execute();
// try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
// blockFfmpeg(br);
// }
log.debug("合并视频成功={}", targetPath);
// } catch (IOException e) {
// throw new Exception("合并视频失败");
// }
// }
public static void picToVideo1(File sourcePath, String pathHead, Integer frame, Integer time, String targetPath) throws Exception {
String tmp = pathHead + IdWorker.getId() + "/";
File file = new File(tmp);
if (!file.exists()) {
file.mkdirs();
}
//生成对应的图片
int i1 = frame * time;
for (int i = 1; i <= i1; i++) {
File file1 = new File(file, (i + ".jpg"));
FileUtils.copyFile(sourcePath, file1);
}
ProcessWrapper ffmpeg=null;
try {
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
ffmpeg.addArgument("-f");
ffmpeg.addArgument("image2");
ffmpeg.addArgument("-i");
ffmpeg.addArgument(tmp + "%d.jpg");
ffmpeg.addArgument("-r");
ffmpeg.addArgument(frame.toString());
ffmpeg.addArgument("-vf");
ffmpeg.addArgument("setsar=1");
ffmpeg.addArgument(targetPath);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
} catch (IOException e) {
throw new Exception("合并视频失败");
} finally {
if (sourcePath != null && sourcePath.exists()) {
sourcePath.delete();
}
File[] files = file.listFiles();
for (File file1 : files) {
file1.delete();
}
file.delete();
if (null!=ffmpeg){
ffmpeg.close();
}
}
}
/**
* 图片宽高修改
*
* @throws Exception
*/
public static void modifyResolution(String imagePath, String resultPath, String width, String height) throws Exception {
ProcessWrapper ffmpeg =null;
try {
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
ffmpeg.addArgument("-protocol_whitelist");
ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
ffmpeg.addArgument("-i");
ffmpeg.addArgument(imagePath);
ffmpeg.addArgument("-vf");
ffmpeg.addArgument(MessageFormat.format("scale={0}:{1}", width, height, "setsar=1"));
ffmpeg.addArgument(resultPath);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
} catch (IOException e) {
throw new Exception("合并视频失败");
}finally{
if (null!=ffmpeg){
ffmpeg.close();
}
}
}
/**
* @return void
* @Author liufangli
* @Description 图片添加文字处理
* @Date 10:34 2022/8/17
* @Param [imagePath, text, targetPath]
**/
// public static void picMergeWords1(String imagePath,String resultPath,String s) throws Exception {
// try {
// ProcessWrapper ffmpeg = new DefaultFFMPEGLocator().createExecutor();
// ffmpeg.addArgument("-protocol_whitelist");
// ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
// ffmpeg.addArgument("-i");
// ffmpeg.addArgument(imagePath);
// int size = 640 / 50;
// String s2 = lineFeed(s, size,0);
// String[] split = s2.split("\n");
// Integer w=640;
// Integer h=50;
// if (split.length==1){
// w=s.length()*50;
// }else {
// h=57*split.length;
// }
// String s1 = "color=White:s="+w+"x"+h+"[v1];[v1]";
// List<String> list = new ArrayList<>();
// for (Integer i=0;i<split.length;i++){
// list.add("drawtext=fontcolor=#AACC00:fontsize=50:fontfile='D\\:/ymt/java/font/d.ttf':line_spacing=7:text='"+split[i]+"':x=0:y="+i*57);
// }
// String join = String.join(",", list);
// s1+=join;
// String s4="[v2]rotate=a=-PI*30/180:fillcolor=White@0[o1];[o1]colorkey=White:0.01:1[o2];[0:v][o2]overlay=x=(W-w)/2:y=(H-h)/2";
// ffmpeg.addArgument("-filter_complex");
// String s3 = "\"" + join + "\"";
// ffmpeg.addArgument(s3);
ffmpeg.addArgument("\"color=White:s=720x1280[v1];[v1]drawtext=fontcolor=black:fontfile='D\\:/ymt/java/font/d.ttf':text='这是什么东西?':x=60:y=50:fontsize=40\"");
// ffmpeg.addArgument("-y");
// ffmpeg.addArgument(resultPath);
// ffmpeg.execute();
// try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
// blockFfmpeg(br);
// }
log.debug("合并视频成功={}", targetPath);
// } catch (IOException e) {
// throw new Exception("合并视频失败");
// }
// }
public static boolean picMergeWords(WordAndCoverStyleDto dto) throws Exception {
Integer fontSize = dto.getCoverTitleSize();
String text = dto.getCoverTitle();
String imagePath = dto.getVideoCover();
String fontPath = dto.getCoverTitleFont();
String targetPath = dto.getTargetCoverUrl();
String fontColor = dto.getCoverTitleColor();
Integer rotate = dto.getCoverTitleRotate();
String bgmColor = dto.getCoverTitleBgmColor();
Integer coverTitleAlign = dto.getCoverTitleAlign();
String noColor = "";
for (int i = 0; i < 3; i++) {
noColor = RandomColor.getColor();
if (!(noColor.equals(fontColor) || noColor.equals(bgmColor))) {
break;
}
}
if (StringUtils.isAnyEmpty(text, imagePath, fontPath, targetPath, fontColor)) {
return false;
}
//计算单行最多几个字
int size = 640 / fontSize;
//字幕超出限定长度640就进行换行
text = lineFeed(text, size, 1);
//分离出每行字幕
String[] split = text.split("\n");
Map<String, Object> map = handleWordsPosition1(dto.getCoverTitleLocation(), split, fontSize, 7);
Object x = map.get("x");
List list = (List) map.get("y");
List list1=new ArrayList();
for (int i=0;i<split.length;i++){
String s = "drawtext=fontfile='" + fontPath + "':text='" + split[i] + "':x=" + x.toString() + ":y=" + list.get(i).toString()+ ":fontsize=" + fontSize + ":fontcolor=" + fontColor + "";
if (StringUtils.isNotEmpty(bgmColor)){
s+=":box=1:boxcolor="+bgmColor+":boxborderw=10";
}else {
s += ":box=0:boxborderw=0";
}
list1.add(s);
}
ProcessWrapper ffmpeg=null;
try {
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
ffmpeg.addArgument("-protocol_whitelist");
ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
ffmpeg.addArgument("-i");
ffmpeg.addArgument(imagePath);
ffmpeg.addArgument("-filter_complex");
ffmpeg.addArgument("\"color="+noColor+":s=720x1280[v1];" +
"[v1]"+String.join(",",list1)+"[o1];" +
"[o1]rotate=a=PI*"+rotate+"/180:fillcolor="+noColor+"@0[o1];[o1]colorkey="+noColor+":0.01:0[o2];[0:v][o2]overlay=x=(W-w)/2:y=(H-h)/2"+
"\"");
ffmpeg.addArgument("-y");
ffmpeg.addArgument(targetPath);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
} catch (IOException e) {
throw new Exception("视频合并封面文字失败");
}finally{
if (null!=ffmpeg){
ffmpeg.close();
}
}
return true;
}
/**
* @return java.util.Map<java.lang.String, java.lang.Object>
* @Author liufangli
* @Description 处理文字所在位置
* @Date 10:41 2022/8/20
* @Param [location 标题所在位置默认0中间,下拉框的值:1左上角,2右上角、3左下角、4右下角、5中上、6中间、7中下、8左中 9右中, map]
**/
private static Map<String, Object> handleWordsPosition(int location, Map<String, Object> map, int i) {
String xLocation = "";
String yLocation = "";
if (0 == location || 6 == location) {
return map;
}
//处理x轴
if (location == 1 || location == 3 || location == 8) {
//x轴左
xLocation = "30";
} else if (location == 2 || location == 4 || location == 9) {
//x轴右
xLocation = "(w-text_w)-text_w*" + i;
} else {
//x轴居中
xLocation = "(w-text_w)/2";
}
//处理y轴
if (location == 1 || location == 5 || location == 2) {
//y轴 上
yLocation = "30";
} else if (location == 3 || location == 4 || location == 7) {
//y轴 下
yLocation = "(h-text_h)-30";
} else {
//x轴居中
yLocation = "(h-text_h)/2+text_h";
}
map.put("x", xLocation);
map.put("y", yLocation);
return map;
}
/**
* 字幕位置
* @param location 标题所在位置 默认0中间,下拉框的值:1左上角,2右上角、3左下角、4右下角、5中上、6中间、7中下、8左中 9右中
* @Param split 字幕内容
* @Param size 字体大小
* @Param num 单行字数
* @Param line 行间距
* @return
*/
private static Map<String, Object> handleWordsPosition1(int location,String[] split,int size,int line) {
Map map=new HashMap();
//x左
if (location == 1 || location == 3 || location == 8) {
map.put("x","30");
}
//x右
else if (location == 2 || location == 4 || location == 9) {
map.put("x","w-text_w-30");
}
//x中
else {
map.put("x","(w-text_w)/2");
}
//y上
if (location == 1 || location == 2|| location == 5) {
List<String> y=new ArrayList<>();
for (int i=0;i<split.length;i++){
y.add("30+" + (i * (size + line)));
}
map.put("y",y);
}
//y下
else if (location == 3 || location == 4|| location == 7){
List<String> y=new ArrayList<>();
for (int i=split.length;i>0;i--){
y.add("h-30-" + (i * (size + line)));
}
map.put("y",y);
}
//y中
else {
List<String> y=new ArrayList<>();
int i1 = split.length * (size + line);
for (int i=0;i<split.length;i++){
y.add("(h-"+i1+")/2+" + (i * (size + line)));
}
map.put("y",y);
}
return map;
}
/**
* 获取视频第一帧
* @throws Exception
*/
public static boolean getPicFromVideo(String sourcePath, String targetPath) throws Exception {
ProcessWrapper ffmpeg =null;
try {
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
ffmpeg.addArgument("-protocol_whitelist");
ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
ffmpeg.addArgument("-i");
ffmpeg.addArgument(sourcePath);
ffmpeg.addArgument("-ss");
ffmpeg.addArgument("1");
ffmpeg.addArgument("-f");
ffmpeg.addArgument("image2");
ffmpeg.addArgument(targetPath);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
} catch (IOException e) {
log.error("截图视频第一帧失败,{}",e.getMessage());
return false;
}finally{
if (null!=ffmpeg){
ffmpeg.close();
}
}
return true;
}
public static void main(String[] args) throws Exception {
}
/**
* 获取视频信息
*
* @param url
* @return
*/
public static MultimediaInfo getVideoInfo(URL url) throws Exception {
log.info("URL:"+url);
try {
MultimediaObject multimediaObject = new MultimediaObject(url);
return multimediaObject.getInfo();
} catch (EncoderException e) {
e.printStackTrace();
log.error("获取视频信息报错={}", e.getMessage());
throw new Exception("获取视频信息报错");
}
}
/**
* 获取视频时长
*/
public static long readVideoTime(String filePath) throws IOException {
ProcessWrapper ffmpeg =null;
try {
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
ffmpeg.addArgument("-i");
ffmpeg.addArgument(filePath);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
StringBuilder outInfo = new StringBuilder();
String line = "";
while ((line = br.readLine()) != null) {
outInfo.append(line);
}
br.close();
// 通过正则获取时长信息
String regexDuration = "Duration: (.*?), start: (.*?), bitrate: (\\d*) kb\\/s";
Pattern pattern = Pattern.compile(regexDuration);
Matcher matcher = pattern.matcher(outInfo.toString());
if (matcher.find()) {
return getTime(matcher.group(1));
}
}
} catch (IOException e) {
e.printStackTrace();
} finally{
if (null!=ffmpeg){
ffmpeg.close();
}
}
return 0;
}
private static long getTime(String time) {
String ZERO = "0";
int min = 0;
String[] strs = time.split(":");
if (strs[0].compareTo(ZERO) > 0) {
// 秒
min += Long.parseLong(strs[0]) * 60 * 60 * 1000;
}
if (strs[1].compareTo(ZERO) > 0) {
min += Long.parseLong(strs[1]) * 60 * 1000;
}
if (strs[2].compareTo(ZERO) > 0) {
min += Math.round(Double.parseDouble(strs[2]) * 1000);
}
return min;
}
/**
* 高斯模糊背景
*/
public static void gaussianBlur(String pathHead, String path, String out, Integer deg) throws Exception {
String tmp1 = pathHead + IdWorker.getId() + ".mp4";
//先生成背景视频
File file0 = new File(path);
File file1 = new File(tmp1);
FileUtils.copyFile(file0, file1);
ProcessWrapper ffmpeg=null;
try {
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
ffmpeg.addArgument("-protocol_whitelist");
ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
ffmpeg.addArgument("-i");
ffmpeg.addArgument(path);
ffmpeg.addArgument("-i");
ffmpeg.addArgument(tmp1);
ffmpeg.addArgument("-filter_complex");
String s = "[0:v]scale=1720:1280[bv0];[bv0]crop=720:1280:500:0[bv1];[bv1]gblur=sigma=" + deg + "[bv2];[bv2]eq=contrast=1:brightness=-0.1[bv];[bv][1:v]overlay=x=0:y=372[out];[out]setsar=1";
ffmpeg.addArgument(s);
ffmpeg.addArgument("-y");
ffmpeg.addArgument(out);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
} catch (IOException e) {
throw new Exception("合并视频失败");
} finally {
if (file1 != null && file1.exists()) {
file1.delete();
}
if (null!=ffmpeg){
ffmpeg.close();
}
}
}
/**
* 高斯模糊背景
*/
public static void gaussianBlur1(String pathHead, String path, String out, Integer deg) throws Exception {
String tmp0 = pathHead + IdWorker.getId() + ".mp4";
String tmp1 = pathHead + IdWorker.getId() + ".mp4";
//先生成背景视频
File file0 = new File(tmp0);
File file1 = new File(tmp1);
HttpUtils.httpDownload(path, file0);
FileUtils.copyFile(file0, file1);
ProcessWrapper ffmpeg =null;
try {
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
ffmpeg.addArgument("-protocol_whitelist");
ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
ffmpeg.addArgument("-i");
ffmpeg.addArgument(path);
ffmpeg.addArgument("-i");
ffmpeg.addArgument(tmp1);
ffmpeg.addArgument("-filter_complex");
String s = "[0:v]scale=-1:1280[bv0];[bv0]crop=720:1280:(iw-720)/2:0[bv1];[bv1]gblur=sigma=" + deg + "[bv2];[bv2]eq=contrast=1:brightness=-0.1[bv];[1:v]scale=720:-1[av];[bv][av]overlay=x=0:y=(1280-h)/2[out];[out]setsar=1";
ffmpeg.addArgument(s);
ffmpeg.addArgument("-r");
ffmpeg.addArgument("25");
ffmpeg.addArgument("-f");
ffmpeg.addArgument("mp4");
ffmpeg.addArgument("-an");
ffmpeg.addArgument(out);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
} catch (IOException e) {
throw new Exception("合并视频失败");
} finally {
if (file0 != null && file0.exists()) {
file0.delete();
}
if (file1 != null && file1.exists()) {
file1.delete();
}
if (null!=ffmpeg){
ffmpeg.close();
}
}
}
/**
* 给视频自定义背景
*/
public static void customBack1(String path, String image, String out) throws Exception {
ProcessWrapper ffmpeg =null;
try {
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
ffmpeg.addArgument("-protocol_whitelist");
ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
ffmpeg.addArgument("-i");
ffmpeg.addArgument(image);
ffmpeg.addArgument("-i");
ffmpeg.addArgument(path);
ffmpeg.addArgument("-filter_complex");
String s = "[0:v]scale=720:1280[bv];[1:v]scale=720:-1[av];[bv][av]overlay=x=0:y=(1280-h)/2[out];[out]setsar=1";
ffmpeg.addArgument(s);
ffmpeg.addArgument("-r");
ffmpeg.addArgument("25");
ffmpeg.addArgument("-f");
ffmpeg.addArgument("mp4");
ffmpeg.addArgument("-an");
ffmpeg.addArgument(out);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
} catch (IOException e) {
throw new Exception("合并视频失败");
}finally{
if (null!=ffmpeg){
ffmpeg.close();
}
}
}
//换行工具
private static String lineFeed(String source, Integer i, int location) {
//遍历每一行
int flag = 0;
StringBuilder res = new StringBuilder(source);
for (int index = 1; index <= (source.length() / i); index++) {
//存在下一行
if (source.length() > (index * i)) {
flag += i;
res = res.insert(flag, '\n');
flag++;
}
}
return res.toString();
}
/**
* 添加字幕
*/
public static void addWords(List<String> words, Double first, String firstFont, Double second, String secondFont, List<Double> offset, String sourcePath, String targetPath, Integer firstSize, Integer secondSize, String firstColor, String secondColor, String firstBackColor, String secondBackColor, Integer firstAlign, Integer secondAlign) throws Exception {
String s1 = "";
String s2 = "";
//不是默认文字背景(默认无背景)
if (!"default".equals(firstBackColor)) {
s1 = ":box=1:boxcolor=" + firstBackColor;
}
//不是默认文字背景(默认无背景)
if (!"default".equals(secondBackColor)) {
s2 = ":box=1:boxcolor=" + secondBackColor;
}
ProcessWrapper ffmpeg =null;
try {
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
ffmpeg.addArgument("-protocol_whitelist");
ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
ffmpeg.addArgument("-i");
ffmpeg.addArgument(sourcePath);
ffmpeg.addArgument("-max_muxing_queue_size");
ffmpeg.addArgument("2048");
ffmpeg.addArgument("-vf");
List<String> list = new ArrayList<>();
Double flag = second;
//700 (视频宽度720 左右宽度留了10 px)
int i1 = 700 / firstSize;
int i2 = 700 / secondSize;
//每行不超过10个
if (i1 > 10) {
i1 = 10;
}
//每行不超过12个
if (i2 > 12) {
i2 = 12;
}
for (int i = 0; i < words.size(); i++) {
//标题
if (i == 0) {
String s = lineFeed(words.get(i), i1, 1);
//靠左
if (firstAlign == 1) {
String[] split = s.split("\n");
for (int k = 0; k < split.length; k++) {
list.add("drawtext=fontcolor=" + firstColor + ":fontsize=" + firstSize + ":fontfile='" + firstFont + "':line_spacing=7" + s1 + ":text='" + split[k] + "':x=10:y=(h-text_h)/6+" + (k * (firstSize + 7)));
}
// list.add("drawtext=fontcolor=" + firstColor + ":fontsize=" + firstSize + ":fontfile='" + firstFont + "':line_spacing=7" + s1 + ":text='" + s + "':x=10:y=(h-text_h)/6");
}
//居中
else if (firstAlign == 2) {
String[] split = s.split("\n");
for (int k = 0; k < split.length; k++) {
list.add("drawtext=fontcolor=" + firstColor + ":fontsize=" + firstSize + ":fontfile='" + firstFont + "':line_spacing=7" + s1 + ":text='" + split[k] + "':x=(w-text_w)/2:y=(h-text_h)/6+" + (k * (firstSize + 7)));
}
}
//靠右
else {
String[] split = s.split("\n");
for (int k = 0; k < split.length; k++) {
String s3 = split[k];
list.add("drawtext=fontcolor=" + firstColor + ":fontsize=" + firstSize + ":fontfile='" + firstFont + "':line_spacing=7" + s1 + ":text='" + s3 + "':x=w-text_w-10:y=(h-text_h)/6+" + (k * (firstSize + 7)));
}
}
}
//字幕
else {
String s = lineFeed(words.get(i), i2, 1);
Double tmp = flag;
flag += offset.get(i - 1);
//靠左
if (secondAlign == 1) {
String[] split = s.split("\n");
for (int k = 0; k < split.length; k++) {
list.add("drawtext=fontcolor=" + secondColor + ":fontsize=" + secondSize + ":fontfile='" + secondFont + "':line_spacing=7" + s2 + ":text='" + split[k] + "':x=10:y=(h-" + (split.length * (secondSize + 7)) + ")*5/6+" + (k * (secondSize + 7)) + ":enable='between(t\\," + tmp + "\\," + flag + ")'");
}
// list.add("drawtext=fontcolor=" + secondColor + ":fontsize=" + secondSize + ":fontfile='" + secondFont + "':line_spacing=7" + s2 + ":text='" + s + "':x=10:y=(h-text_h)*5/6:enable='between(t\\," + tmp + "\\," + flag + ")'");
}
//居中
else if (secondAlign == 2) {
String[] split = s.split("\n");
for (int k = 0; k < split.length; k++) {
list.add("drawtext=fontcolor=" + secondColor + ":fontsize=" + secondSize + ":fontfile='" + secondFont + "':line_spacing=7" + s2 + ":text='" + split[k] + "':x=(w-text_w)/2:y=(h-" + (split.length * (secondSize + 7)) + ")*5/6+" + (k * (secondSize + 7)) + ":enable='between(t\\," + tmp + "\\," + flag + ")'");
}
}
//靠右
else {
String[] split = s.split("\n");
for (int k = 0; k < split.length; k++) {
String s3 = split[k];
list.add("drawtext=fontcolor=" + secondColor + ":fontsize=" + secondSize + ":fontfile='" + secondFont + "':line_spacing=7" + s2 + ":text='" + s3 + "':x=w-text_w-10:y=(h-" + (split.length * (secondSize + 7)) + ")*5/6+" + (k * (secondSize + 7)) + ":enable='between(t\\," + tmp + "\\," + flag + ")'");
}
}
}
}
ffmpeg.addArgument(String.join(",", list));
ffmpeg.addArgument("-y");
ffmpeg.addArgument(targetPath);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
// log.debug("添加字幕成功={}", targetPath);
} catch (IOException e) {
throw new Exception("添加字幕失败");
}finally{
if (null!=ffmpeg){
ffmpeg.close();
}
}
}
/**
* 视频滤镜
*/
public static void videoFilter(String path, String out, List<String> filter) throws Exception {
Random random = new Random();
ProcessWrapper ffmpeg =null;
try {
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
ffmpeg.addArgument("-protocol_whitelist");
ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
ffmpeg.addArgument("-i");
ffmpeg.addArgument(path);
ffmpeg.addArgument("-vf");
ffmpeg.addArgument(filter.get(random.nextInt(filter.size())));
ffmpeg.addArgument("-y");
ffmpeg.addArgument(out);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
} catch (IOException e) {
throw new Exception("合并视频失败");
}finally{
if (null!=ffmpeg){
ffmpeg.close();
}
}
}
/**
* 合并音频
* 混音效果,长度以originAudioPath的音频为主
*
* @param originAudioPath 音频url或本地路径
* @param magicAudioPath 音频url或本地路径
* @param audioTargetPath 目标存储本地路径
*/
public static void mixAudio(
String pathHead, String originAudioPath, String magicAudioPath, String audioTargetPath) throws Exception {
String tmp = pathHead + IdWorker.getId() + ".aac";
ProcessWrapper ffmpeg =null;
try {
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
//去除静默
ffmpeg.addArgument("-i");
ffmpeg.addArgument(originAudioPath);
ffmpeg.addArgument("-c:v");
ffmpeg.addArgument("copy");
ffmpeg.addArgument("-ac");
ffmpeg.addArgument("2");
ffmpeg.addArgument(tmp);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
ffmpeg.close();
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
ffmpeg.addArgument("-protocol_whitelist");
ffmpeg.addArgument("concat,file,http,https,tcp,tls,crypto");
ffmpeg.addArgument("-i");
ffmpeg.addArgument(tmp);
ffmpeg.addArgument("-i");
ffmpeg.addArgument(magicAudioPath);
ffmpeg.addArgument("-filter_complex");
ffmpeg.addArgument("amix=inputs=2:duration=first:dropout_transition=2");
ffmpeg.addArgument("-f");
ffmpeg.addArgument("mp3");
ffmpeg.addArgument(audioTargetPath);
ffmpeg.execute();
try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
blockFfmpeg(br);
}
// log.debug("合并音频={}", audioTargetPath);
} catch (IOException e) {
throw new Exception("合并音频失败");
} finally {
File file = new File(tmp);
if (file.exists()) {
file.delete();
}
if (null!=ffmpeg){
ffmpeg.close();
}
}
}
}