@是
windows下通过java调用ffmpeg实现视频水印功能
最近接到领导的任务:要求实现视频加水印,原本考虑使用javacv实现每一帧抓取在进行帧运算等操作,后来尝试了,太麻烦果断采用java的Process类去驱动ffmpeg程序。于是先打算在windows本机环境下做一个demo,然后再想法改成生产环境能够使用。但是遇到了一大堆坑。下面是本人做的总结。希望能对新手同志们有所帮助。
1.安装ffmpeg并且在网上找到有关调用代码
ffmpeg在windows下安装较简单,直接在网上下载压缩包后解压到指定路径即可。
这里我解压到了D:\Program Files\ffmpeg-n4.4-latest-win64-gpl-shared-4.4
主干程序
class PrintStream extends Thread
{
java.io.InputStream __is = null;
public PrintStream(java.io.InputStream is)
{
__is = is;
}
public void run()
{
try
{
while(this != null)
{
int _ch = __is.read();
if(_ch != -1)
System.out.print((char)_ch);
else break;
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
public class test2 {
public final static String ffmpeg_path="D:/Program Files/ffmpeg-n4.4-latest-win64-gpl-shared-4.4/bin/ffmpeg.exe ";
public final static String input="E:/视频/pp.mp4";
public final static String output= "E:\\视频\\grr.mp4";
public final static String logo = "E\\\\:/视频/12.png";
public static void main(String[] args) {
String command = ffmpeg_path+"-i "+input+" -vf \"movie="+
logo+"[watermark];[in][watermark] " +
"overlay=10:10 [out]\" -codec:a copy "+output;
try {
Runtime run = Runtime.getRuntime();
System.out.println(command);
java.lang.Process process = run.exec(command);
new PrintStream(process.getErrorStream()).start();
new PrintStream(process.getInputStream()).start();
process.waitFor();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
这是参考:
这位博主在遇到坑的时候都写了,还比较全面。不过我直接调用他的代码还是无法实现视频加水印的问题。
遇到的常见问题:
- 写路径的时候采用:
\写法在写ffmpeg路径、输入的原视频路径、输出的加了水印的视频路径,这三个地方正常写没有问题,因为他是识别java调用的方法去驱动的程序的位置。但是在写logo(即要加的水印的位置)的时候不能按照原来的\写路径。因为在在ffmpeg命令中它是在双引号里的,ffmpeg的movie filter读取规则是按照linux来的,不支持对盘符(C:、D:、E:)这些的读取。可参考:
如果按这样会报错:
所以吧logo路径改成:
public final static String logo = "E\\\\:/视频/12.png";
然后还有一种最常见的错误就是路径写错
比如空格少了造成连读等因素。这个可以自己排查。还有就是文件读取权限等,这种情况比较少见,反正我是没遇到过。当然第一种情况也会造成“系统找不到指定的文件”这样的报错。
程序做一下封装就成了下面的样子
参考了:
public class ProcessExec {
private Process process;
public static class StringUtils{
public static boolean isNotEmpty(String s){
if (s==null||s.length()==0)
return false;
return true;
}
}
public void execute(Map<String,String> dto)
{
StringBuffer waterlogo = new StringBuffer();
waterlogo.append("-i ");
if(null!=dto.get("input_path")&&StringUtils.isNotEmpty(dto.get("input_path"))){
waterlogo.append(dto.get("input_path"));
}
waterlogo.append(" -vf \"movie=");
if (null!=dto.get("logo")&&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-10:main_h-overlay_h-10 [out]\" -codec:a copy ");
if (null!=dto.get("video_converted_path")&&StringUtils.isNotEmpty(dto.get("video_converted_path"))){
waterlogo.append(dto.get("video_converted_path"));
}
Runtime run = Runtime.getRuntime();
String ffmegPath = null;
if (StringUtils.isNotEmpty(dto.get("ffmpeg_path"))){
ffmegPath = dto.get("ffmpeg_path");
}
// 执行命
try {
System.out.println(ffmegPath+waterlogo);
java.lang.Process process = run.exec(ffmegPath+waterlogo);
// Process videoProcess = new ProcessBuilder(ffmegPath+waterlogo).redirectErrorStream(true).start();
new PrintStream(process.getErrorStream()).start();
new PrintStream(process.getInputStream()).start();
process.waitFor();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class PrintStream extends Thread
{
java.io.InputStream __is = null;
public PrintStream(java.io.InputStream is)
{
__is = is;
}
public void run()
{
try
{
while(this != null)
{
int _ch = __is.read();
if(_ch != -1)
System.out.print((char)_ch);
else break;
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
public class test {
public static void main(String[] args) {
ProcessExec ps = new ProcessExec();
HashMap<String, String> dto=new HashMap<String, String>();
dto.put("ffmpeg_path","D:/Program Files/ffmpeg-n4.4-latest-win64-gpl-shared-4.4/bin/ffmpeg.exe ");//必填:此处是ffmpeg.exe所在位置,也就FFmpeg文件夹bin目录下的ffmpeg.exe
// dto.put("ffmpeg_path","E:\\ffmpeg.exe ");
dto.put("input_path", "E:/pp.mp4");//必填;此处是你要处理的视频位置
dto.put("video_converted_path", "E:\\grr.mp4");//必填;此处是完成添加水印后输入视频的位置并重新命名该视频
dto.put("logo", "E\\\\:/12.png");//必填;此处是你要添加的水印位置,注意此处图片位置一定要加上转译符,否则识别不了盘符
ps.execute(dto);
}
}
这样就能正常跑起来了,不过要注意的是:我这里直接输出的路径是E:/grr.mp4,输出文件的路径下不能有重复的grr.mp4文件.因为我这里没有做覆盖命令,所以要确保你的目录下没有grr.mp4.不然控制台让你输入y/N。你又输入不了了。
看到控制台打印这个就知道成功了!
另外如果使用ProcessBuilder类的话,一定要用字符串数组的形式传递命令的参数,因为系统调用的时候第一个字符串默认会被认为是ffmpeg的路径。
可参考下: