实际业务需求:
在存储视频文件时先对上传的文件做加水印处理,之前做过通过七牛云服务器加水印的功能,虽然可以完成需求,但是毕竟是收费的呀!以下介绍在上传加水印的视频前对视频加水印的过程,至于上传功能,可根据自己的需求上传七牛或者阿里的OSS都可以,不在这里讲述。
现将ffmpeg加水印的代码献上。仅供参考,希望能帮到你。。。。如下:
//加水印的请求方法:
private String toAddLogoVideo() {
//本地视频-----待加水印的视频
String videoLocalPath = "D:\\sjb\processing\\video\\12344.mp4";
//本地水印logo
String logoLocalPath = "D:\\sjb\processing\\1624325924993.png";
//加水印的方法
String fileName = videoAddLogo(logoLocalPath, videoLocalPath);
return "D:\\sjb\processing\\" + fileName;
}
/**
* @Description :视频添加水印方法
* @param: [
* logoPath: 水印地址,本地磁盘地址 如:C:\\video\\xxx.mp4
* , videoPath 视频地址,本地磁盘地址 如:D:\\video\\xxx.mp4
* ]
*/
public String videoAddLogo(String logoPath, String videoPath) {
//获取随机值
String uuid = UUID.randomUUID().toString();
HashMap<String, Object> map = FFMPEG.getWidthHeight(videoPath);
//视频宽
Integer videoWidth = (Integer) map.get("width");
//视频高
Integer videoHeight = (Integer) map.get("height");
//根据不同的视频生成相应的logo,存放在本地磁盘
String newLogo = markVideoByLogoType(
logoPath
, "D:\\sjb\\logo\\" + uuid + ".png"
, videoWidth, videoHeight
, "D:\\sjb\\processing\\"
, uuid + ".png"
);
//创建给视频加水印的方法
FFMPEG ff = new FFMPEG();
//加水印后的视频本地缓存地址
String videoConvertedPath = "D:\\sjb\\processing\\" + uuid + ".mp4";
HashMap<String, String> dto = new HashMap<String, String>();
//待处理的视频地址
dto.put("input_path", videoPath);
//设置宽高
dto.put("screen_size", videoWidth.toString() + "x" + videoHeight.toString());
dto.put("xaxis", "0");
dto.put("yaxis", "0");
//修改后与的地址
dto.put("video_converted_path", videoConvertedPath);
if (map.get("type").equals(1)) {
//插件地址
dto.put("ffmpeg_path", "D:\\VoidUtil\\bin\\ffmpeg.exe");
//是win logo地址
dto.put("logo", "D\\\\://sjb/processing/" + uuid + ".png");
ff.videoTransfer(dto);
//删除缓存文件
new File(newLogo).delete();
return uuid + ".mp4";
} else {
//等比放大后再加
ff.videoTransfer(dto);
//等比放大后视频缓存地址:重命名放大后的加水印文件,避免与videoConvertedPath重复,导致加水印失败。。。
String videoNewPath = "D:\\sjb\\processing\\" + uuid + "_.mp4";
//插件地址
dto.put("ffmpeg_path", "D:\\VoidUtil\\bin\\ffmpeg.exe");
//是win logo地址
dto.put("logo", "D\\\\://sjb/processing/" + uuid + ".png");
dto.put("input_path", videoConvertedPath);
dto.put("video_converted_path", videoNewPath);
ff.videoTransfer(dto);
//删除缓存文件
new File(newLogo).delete();
return uuid + "_.mp4";
}
}
/**
* @Description: 按照视频的分辨率生成大小适宜的水印
* @Author: wyc
*/
public static String markVideoByLogoType(String icon, String newIcon, Integer tW,
Integer tH, String output, String imageName) {
try {
//优先创建newicon文件:等比放大或缩小调整之后的水印logo
File ficon = new File(newIcon);
if (!ficon.exists()) {
ficon.createNewFile();
}
//参照要加水印的文件,将logo做等比放大或缩小
resizeImage(icon, newIcon, (int) tW);
// 创建等比缩放后的Logo的Image对象
Image ic = ImageIO.read(ficon);
//logo图高度
int sH = ic.getHeight(null);
//logo图宽度
int sW = ic.getWidth(null);
//创建要加水印的文件的画布
BufferedImage bi = new BufferedImage(tW, tH, BufferedImage.TYPE_INT_RGB);
// 创建一个指定 BufferedImage 的 Graphics2D 对象
Graphics2D g = bi.createGraphics();
bi = g.getDeviceConfiguration().createCompatibleImage(tW, tH, Transparency.TRANSLUCENT);
g = bi.createGraphics();
// x,y轴默认是从0坐标开始 第一行水印初始横坐标
int x = (int) (sH / 3.5);
// 底图的高度
int y = (int) (sW / 1.5);
//水印图象的路径 水印一般为gif或者png的,这样可设置透明度
ImageIcon imgIcon = new ImageIcon(newIcon);
//得到Image对象。
Image con = imgIcon.getImage();
g.drawImage(con, x, y, null);
g.dispose();
try {
//保存原图 输出 文件 到指定的路径
saveAs(bi, "png", new File(output + imageName));
} catch (Exception e) {
e.printStackTrace();
}
ficon.delete();
} catch (Exception e) {
e.printStackTrace();
}
return output + imageName;
}
/**
* 修改logo的像素
*
* @param srcPath 源图片路径
* @param desPath 修改大小后图片路径
* @param scaleSize 图片的修改比例,目标宽度
*/
public static void resizeImage(String srcPath, String desPath, int scaleSize) throws IOException {
File srcFile = new File(srcPath);
Image srcImg = ImageIO.read(srcFile);
BufferedImage bi = null;
try {
bi = ImageIO.read(srcFile);
} catch (Exception e) {
e.printStackTrace();
}
float width = bi.getWidth(); // 像素
float height = bi.getHeight(); // 像素
float scale = width / scaleSize;
BufferedImage buffImg = null;
buffImg = new BufferedImage(scaleSize, (int) (height / scale), BufferedImage.TYPE_INT_RGB);
//保持Png图片的透明背景属性
Graphics2D g = buffImg.createGraphics();
buffImg = g.getDeviceConfiguration().createCompatibleImage(scaleSize, (int) (height / scale), Transparency.TRANSLUCENT);
g = buffImg.createGraphics();
//使用TYPE_INT_RGB修改的图片会变色
buffImg.getGraphics().drawImage(
srcImg.getScaledInstance(scaleSize, (int) (height / scale), Image.SCALE_SMOOTH), 0,
0, null);
//原图保存
try {
// 输出 文件 到指定的路径
FileOutputStream out = new FileOutputStream(desPath);
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(buffImg);
param.setQuality(1, true);
encoder.encode(buffImg, param);
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//------------------------FFMPEG-------工具类--begin---
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* FFMPEG转码的方式给视频加水印
*/
public class FFMPEG {
private static final boolean isWindows = System.getProperty("os.name").toLowerCase().contains("win");
public static String returnSecond(String instr) {
String returnValue = null;
if (null != instr) {
String[] a = instr.split("\\.");
String[] b = a[0].split(":");
int returnNumber = 0;
if (null != instr && b[0].length() != 0) {
returnNumber = Integer.valueOf(b[0]) * 60 * 60 + Integer.valueOf(b[1]) * 60 + Integer.valueOf(b[2]);
returnValue = String.valueOf(returnNumber);
} else {
returnValue = null;
}
}
return returnValue;
}
/**
* @ HashMap<String,String> dto 参数传递对象<br>
* dto中包含的参数<br>
* (必填)1.ffmpeg_path:ffmpeg执行文件地址,如 d:\\ffmpeg\\ffmpeg.exe
* Linux下直接调用ffmpeg命令(当然你事先已经有这个程序了)<br>
* (必填)2.input_path:原视频路径<br>
* (必填)3.video_converted_path:转换后视频输出路径<br>
* (可选)4.screen_size:视频尺寸 长度乘宽度 乘号用英文小写"x"如 512x480<br>
* (可选)5.logo:水印地址(其实在ffmpeg中有一个专门的watermark参数,logo跟它有何不同,我还没看,不过对我来说效果一样
* 貌似需要png图片才行)<br>
* (可选,如果填写必须有logo才行,默认为0)6.xaxis:水印logo的横坐标(只有logo参数为一个正确路径才行) 比如0<br>
* (可选,如果填写必须有logo才行,默认为0)6.yaxis:水印logo的纵坐标(只有logo参数为一个正确路径才行) 比如0<br>
* (可选)vb:视频比特率,传入一个数值,单位在程序里面拼接了k (可选)ab:音频比特率,传入一个数值,单位在程序里面拼接了k
*/
public String videoTransfer(HashMap<String, String> dto) {
List<String> cmd = new ArrayList<String>();
if (isWindows) {
//是win
cmd.add(dto.get("ffmpeg_path"));
} else {
cmd.add("ffmpeg");
}
//设置水印每隔1秒钟从左移动右直至消失
cmd.add("-y");
cmd.add("-i");
cmd.add(dto.get("input_path"));
if (null != dto.get("screen_size")) {
//-s:截取指定时间的缩略图,-ss后跟的时间单位是秒
cmd.add("-s");
cmd.add(dto.get("screen_size"));
}
if (null != dto.get("logo")) {
String logo = dto.get("logo");
//-vf:视频旋转
cmd.add("-vf");
String xaxis = dto.get("xaxis");
String yaxis = dto.get("yaxis");
xaxis = xaxis != null && !"".equals(xaxis) ? xaxis : "0";
yaxis = yaxis != null && !"".equals(yaxis) ? yaxis : "0";
String logoString = "movie=" + logo + "[logo],[in][logo]overlay=x=" + xaxis + ":y=" + yaxis + "[out]";
cmd.add(logoString);
}
cmd.add("-strict");
cmd.add("-2");
if (null != dto.get("vb") && !"".equals(dto.get("vb"))) {
cmd.add("-vb");
cmd.add(dto.get("vb") + "k");
}
if (null != dto.get("ab") && !"".equals(dto.get("ab"))) {
cmd.add("-ab");
cmd.add(dto.get("ab") + "1500");
}
cmd.add("-q:v");
cmd.add("4");
cmd.add(dto.get("video_converted_path"));
for (String a : cmd) {
System.out.println(a);
}
String converted_time = CmdExecuter.exec(cmd);
//插入水印花费的时间
return returnSecond(converted_time);
}
/**
* 视频等比放大缩小
*
* @param filePath
* @return
*/
public static HashMap<String, Object> getWidthHeight(String filePath) {
File file = new File(filePath);
Encoder encoder = new Encoder();
FileChannel fc = null;
try {
MultimediaInfo m = encoder.getInfo(file);
long ls = m.getDuration();
FileInputStream fis = new FileInputStream(file);
fc = fis.getChannel();
BigDecimal fileSize = new BigDecimal(fc.size());
HashMap<String, Object> map = new HashMap<String, Object>();
if (m.getVideo().getSize().getWidth() < 1000) {
//宽度小于1000px 则等比放大
//float width = m.getVideo().getSize().getWidth() * 2;
float width = 1500.0f;
float height = m.getVideo().getSize().getHeight() * (width / m.getVideo().getSize().getWidth());
int height1 = (int) height;
if (height1 % 2 != 0) {
height1 = height1 + 1;
}
map.put("width", (int) width);
map.put("height", height1);
map.put("screen_size", (int) width + "x" + height1);
//两次--先等比放大,再加水印
map.put("type", 2);
} else {
map.put("width", m.getVideo().getSize().getWidth());
map.put("height", m.getVideo().getSize().getHeight());
map.put("screen_size", m.getVideo().getSize().getWidth() + "x" + m.getVideo().getSize().getHeight());
//一次--直接加水印
map.put("type", 1);
}
return map;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != fc) {
try {
fc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
}
/**
* 保存图片
*
* @param image 图片
* @param formatName 格式名称
* @param outFile 输入文件
* @throws IOException
*/
public static void saveAs(BufferedImage image, String formatName, File outFile) throws IOException {
//重画一下,要么会变色
if (formatName.equalsIgnoreCase("jpg") || formatName.equalsIgnoreCase("jpeg")) {
BufferedImage tag;
tag = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_BGR);
Graphics g = tag.getGraphics();
// 绘制缩小后的图
g.drawImage(image, 0, 0, null);
g.dispose();
image = tag;
}
ImageIO.write(image, formatName, outFile);
}
//------------------------FFMPEG-------工具类--end---
备注: 使用ffmpeg给视频加水印时需要下载不同的ffmpeg.exe。我这里是在widows系统完成的加水印的操作,直接下载接到对应的磁盘(如:本案例中使用的 D:\VoidUtil\bin 即本人下载的ffmpeg存放的位置),Linux系统的安装及配置请自行百度,不再赘述!!