一. windows使用
1. 安装包
链接:https://pan.baidu.com/s/1igxky8Wxhd5wFRLN9_YSgA
提取码:0qo8
ffmpeg-2021-12-12-git-996b13fac4-full_build.7z 解压
bin路径下:
2. 工具类
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* @ClassName: ResultStreamHandler
* @Description
* @Author: 王志亭
* @Date: 2021-12-16 16:31
* @Version 1.0
**/
public class ResultStreamHandler implements Runnable{
private InputStream inputStream;
ResultStreamHandler(InputStream inputStream ) {
this.inputStream = inputStream;
}
public void run() {
BufferedReader bufferedReader = null;
try {
bufferedReader = new BufferedReader( new InputStreamReader( inputStream,"utf-8" ) );
String line = null;
while ( ( line = bufferedReader.readLine() ) != null ) {
System.out.println( line );
}
}
catch ( Throwable t ) {
}
finally {
try {
bufferedReader.close();
}
catch ( IOException e ) {
}
}
}
/**
* 处理process输出流和错误流,防止进程阻塞
* 在process.waitFor();前调用
* @param process
*/
public static void dealStream(Process process) {
if (process == null) {
return;
}
// 处理InputStream的线程
new Thread() {
@Override
public void run() {
System.out.println("run 111111111");
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = null;
try {
while ((line = in.readLine()) != null) {
// logger.info("output: " + line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
// 处理ErrorStream的线程
new Thread() {
@Override
public void run() {
System.out.println("run 222222222");
BufferedReader err = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String line = null;
try {
while ((line = err.readLine()) != null) {
// logger.info("err: " + line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
err.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
ResultStreamHandler 工具类
public interface CmdOutputGetter {
public void dealLine(String str);
}
CmdOutputGetter
import lombok.extern.slf4j.Slf4j;
import java.io.BufferedReader;
import java.io.InputStreamReader;
/**
* @ClassName: CmdExecutor
* @Description FFmpeg 脚本命令执行器
* @Author: 王志亭
* @Date: 2021-12-18 15:17
* @Version 1.0
**/
@Slf4j
public class CmdExecutor {
/**
* CMD操作
* @param getter 获取控制台打印信息
* @param cmd 命令
*/
public static void exec(CmdOutputGetter getter, String... cmd) {
try {
// 创建新线程
ProcessBuilder builder = new ProcessBuilder();
// 执行命令
builder.command(cmd);
builder.redirectErrorStream(true);
Process proc = builder.start();
BufferedReader stdout = new BufferedReader(new InputStreamReader(proc.getInputStream()));
String line;
while ((line = stdout.readLine()) != null) {
if (getter != null)
getter.dealLine(line);
}
proc.waitFor();
stdout.close();
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
CmdExecutor 工具类
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @ClassName: FFmpegUtil
* @Description ffmpeg 工具类
* @Author: 王志亭
* @Date: 2022-04-09 15:09
* @Version 1.0
**/
@Slf4j
@Service("ffmpegUtil")
public class FFmpegUtil {
@Value("${ffmpeg.file}")
private String FFMPEG_FILE;
// 视频水印文件路径
@Value("${ffmpeg.video.water.file}")
private String VIDEO_WATER_FILE;
// 音频水印文件路径
@Value("${ffmpeg.audio.water.file}")
private String AUDIO_WATER_FILE;
// 图片水印
@Value("${ffmpeg.images.water.file}")
private String IMAGES_WATER_FILE;
/**
* @Author: 王志亭
* @Description: 图片添加图片水印
* scale=180:136 水印文件宽高
* 图片水印的矩阵坐标设置
* “overlay=10:main_h-overlay_h-10"; //左下
*
* "overlay=main_w/2-overlay_w/2:main_h-overlay_h-10"; //正下
*
* "overlay=main_w-overlay_w-10:main_h-overlay_h-10"; //右下
*
* "overlay=10:main_h/2-overlay_h/2"; //中左
*
* "overlay=main_w/2-overlay_w/2:main_h/2-overlay_h/2"; //正中
*
* "overlay=main_w-overlay_w-10:main_h/2-overlay_h/2"; //中右
*
* "overlay=10:10"; //左上
*
* "overlay=main_w/2-overlay_w/2:10"; //正上
*
* "overlay=main_w-overlay_w-10:10"; //右上
* @Date: 2022-4-13 15:03
* @return void
**/
public boolean imagesShuiyin(String imagesFile, String imagesWaterFile){
String cmd = FFMPEG_FILE + " -i " + addQuotes(imagesFile) + " -vf \"movie=" + IMAGES_WATER_FILE + ",scale=180:136[watermask];[in][watermask] overlay=main_w-overlay_w-10:10[out]\" -y " + addQuotes(imagesWaterFile);
// String cmd = FFMPEG_FILE + " -i " + addQuotes(imagesFile) + " -vf \"movie=C\\\\:/waterfile.png,scale=180:136[watermask];[in][watermask] overlay=main_w-overlay_w-10:10[out]\" -y " + addQuotes(imagesWaterFile);
System.out.println("cmd *********************** " + cmd);
boolean flag = generalCommand(cmd);
return flag;
}
/**
* @Author: 王志亭
* @Description: 音频添加水印 新版本
* @Date: 2022-4-9 17:52
* @param audioFile: 源音频文件
* @param audioWaterFile: 添加水印后的音频文件
* @return void
**/
public boolean audioShuiYinNew(String audioFile, String audioWaterFile){
String cmd = FFMPEG_FILE + " -i " + addQuotes(audioFile) + " -i " + addQuotes(AUDIO_WATER_FILE)
+ " -filter_complex \"[0:0] [1:0] concat=n=2:v=0:a=1 [a]\" -map \"[a]\" \"-ar\" 48000 -y "
+ addQuotes(audioWaterFile);
boolean flag = generalCommand(cmd);
return flag;
}
/**
* @Author: 王志亭
* @Description: 音频添加水印 == 旧版
* @Date: 2022-4-9 17:14
* @param audioFile: 源音频文件
* @param audioWaterFile: 添加水印后的音频文件
* @return void
**/
public void audioShuiYinOld(String audioFile, String audioWaterFile){
String[] command4concat = {FFMPEG_FILE, " -i ", AUDIO_WATER_FILE.replaceAll(" ","\" \""), "-i", audioFile.replaceAll(" ","\" \""), "-filter_complex",
"[0:0] [1:0] concat=n=2:v=0:a=1 [a]", "-map", "[a]", "-ar", "48000", audioWaterFile, "-y"};
CmdExecutor.exec(new CmdOutputGetter() {
@Override
public void dealLine(String line) {
//把cmd输出的信息每行syso,这个是实时输出的,可以换其他的处理方式
System.out.println(line);
}
}, command4concat);
log.info("水印连接「{}秒」间隔音频完成!");
}
/**
* @Author: 王志亭
* @Description: 视频 添加水印
* @Date: 2021-12-19
* @param input_path: 源文件路径
* @param video_converted_path: 添加水印后的视频路径
* @return void
**/
public boolean videoShuiYin(String input_path, String video_converted_path) {
HashMap<String, String> dto=new HashMap<String, String>();
dto.put("ffmpeg_path",FFMPEG_FILE);//必填:此处是ffmpeg.exe所在位置,也就FFmpeg文件夹bin目录下的ffmpeg.exe
dto.put("input_path", input_path);//必填;此处是你要处理的视频位置
dto.put("video_converted_path", video_converted_path);//必填;此处是完成添加水印后输入视频的位置并重新命名该视频
dto.put("logo", VIDEO_WATER_FILE);//必填;此处是你要添加的水印位置,注意此处图片位置一定要加上转译符,否则识别不了盘符
boolean flag = execute(dto);
return flag;
}
/**
* @Author: 王志亭
* @Description: 视频添加水印
* @Date: 2021-12-23 17:37
* @param dto:
* @return void
**/
private boolean execute(Map<String,String> dto)
{
StringBuffer waterlogo = new StringBuffer();
waterlogo.append(" -i ");
if(null!=dto.get("input_path")&& org.apache.commons.lang.StringUtils.isNotEmpty(dto.get("input_path"))){
waterlogo.append(addQuotes(dto.get("input_path")));
}
waterlogo.append(" -vf \"movie=");
String xaxis = "main_w-overlay_w-10";
String yaxis = "10";
if (null!=dto.get("logo")&& org.apache.commons.lang.StringUtils.isNotEmpty(dto.get("logo"))){
waterlogo.append(dto.get("logo"));
}
waterlogo.append(",scale= 60: 30");
waterlogo.append(" [watermark]; [in][watermark] overlay=main_w-overlay_w-5:5 [out]\" -y ");
if (null!=dto.get("video_converted_path")&& org.apache.commons.lang.StringUtils.isNotEmpty(dto.get("video_converted_path"))){
waterlogo.append(addQuotes(dto.get("video_converted_path")));
}
String ffmegPath = null;
if (StringUtils.isNotEmpty(dto.get("ffmpeg_path"))){
ffmegPath = dto.get("ffmpeg_path");
}
System.out.println("ffmegPath+waterlogo ===== " + ffmegPath+waterlogo);
boolean flag = generalCommand(ffmegPath + waterlogo);
return flag;
}
/**
* @Author: 王志亭
* @Description: 文件路径出现空格 处理方式
* @Date: 2021-12-23 16:05
* @param in: 文件路径
* @return java.lang.String
**/
public static String addQuotes(String in ) {
return "\"" + in + "\"";
}
/**
* @Author: 王志亭
* @Description: 通用命令
* @Date: 2021-12-23 17:35
* @param command: ffmpeg 命令
* @return void
**/
public boolean generalCommand(String command){
boolean flag = false;
Runtime run = Runtime.getRuntime();
InputStream inputStream = null;
InputStream errorStream = null;
try {
java.lang.Process process = run.exec(command);
// 异步读取输出
inputStream = process.getInputStream();
errorStream = process.getErrorStream();
ExecutorService service = Executors.newFixedThreadPool(2);
ResultStreamHandler inputStreamHandler = new ResultStreamHandler(inputStream);
ResultStreamHandler errorStreamHandler = new ResultStreamHandler(errorStream);
service.execute(inputStreamHandler);
service.execute(errorStreamHandler);
new Thread(errorStreamHandler, "error stream").start();
new Thread(inputStreamHandler, "output stream").start();
process.waitFor();
service.shutdownNow();
flag = true;
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
try {
errorStream.close();
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return flag;
}
}
FFmpegUtil 工具类
3. application.yml 配置文件中添加 ffmpeg
# ffmpeg 路径
ffmpeg:
file: E:\wangzhiting\FFmpeg\bin\ffmpeg.exe
# 视频水印文件路径
video:
water:
file: C\\:/water.png
# 音频水印文件路径
audio:
water:
file: E:\音频水印.mp3
# 图片水印文件路径 注意:路径 两个反斜杠必须添加
images:
water:
file: C\\:/waterfile.png
* 注意:在windows下使用图片、视频文件添加图片水印时 盘符路径下必须添加两个反斜杠(\\)例如:C\\:/
4. 注意事项
(1)水印添加完的文件自动生成,ffmpeg命令执行之前生成文件会报错。
例如:如下代码
audioWaterFile:c:/file/1238795.jpg 中
1238795.jpg 文件是自动生成的,
c:/file/ 必须事先生成,不然会报文件或文件夹不存在
public boolean audioShuiYinNew(String audioFile, String audioWaterFile){
String cmd = FFMPEG_FILE + " -i " + addQuotes(audioFile) + " -i " + addQuotes(AUDIO_WATER_FILE)
+ " -filter_complex \"[0:0] [1:0] concat=n=2:v=0:a=1 [a]\" -map \"[a]\" \"-ar\" 48000 -y "
+ addQuotes(audioWaterFile);
boolean flag = generalCommand(cmd);
return flag;
}
解决:可以添加 -y 解决文件
-y: 覆盖文件
二. linux 使用
1. 安装包
链接:https://pan.baidu.com/s/1PshVBEZ4W4eDqEXxsAw2uw
提取码:jr51
ffmpeg-4.4.tar.gz 解压后的文件
前期准备:
在/usr/local/下新建一个ffmpeg的文件夹,并进入到ffmpeg文件夹;
安装gcc(yum install -y gcc)工具;
安装wget(yum -y install wget)工具;
安装(vim)yum install -y vim;
安装ffmpeg
①:将ffmpeg-4.1.3.tar.bz2上传到ffmpeg文件夹下
②:tar -xjvf ffmpeg-4.1.3.tar.bz2
③: cd ffmpeg-4.1.3
④:./configure --enable-shared --prefix=/monchickey/ffmpeg
⑤:make(花费时间会有点长)
⑥:make install
ffmpeg安装完毕
ffmpeg文件
查看一下发现有bin、include、lib、share这4个目录,其中bin是ffmpeg主程序二进制目录,include是C/C++头文件目录,lib是编译好的库文件目录,share是文档目录
配置ffmpeg的环境变量
①:完成以上步奏之后,还不能直接在linux任何位置使用ffmpeg命令,因为还没有配置ffmpeg环境变量,所以这里在原文基础上,新增环境变量这一步;
②:切换root用户,执行命令:"vim /etc/profile";在文件末尾加上下面的命令行:
#set my ffmpeg environment
export PATH=$PATH:/monchickey/ffmpeg/bin
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/monchickey/ffmpeg/lib
③:然后执行生效命令:"source /etc/profile";
④:最后测试一下是否已经生效,在随便一个路径下执行命令:"ffmpeg -version";出现以下版本信息,说明ffmpeg环境变量配置成功,可直接在linux使用命令调用ffmpeg程序;
2. 使用方法跟windows不同的地方
(1)application.yml ffmpeg 配置
# ffmpeg 路径
ffmpeg:
file: /usr/local/ffmpeg/bin/ffmpeg
# 视频水印文件路径
video:
water:
file: /usr/local/tomcat-news-server/water.png
audio:
water:
file: /usr/local/tomcat-news-server/音频水印.mp3
images:
water:
file: /usr/local/tomcat-news-server/waterfile.png
(2)FFmpegUtil 工具类中的 public boolean generalCommand(String command) 方法
windows 下的是:
linux 下 是:
FFmpegUtil linux 下使用 工具类