@是
windows下通过java调用ffmpeg实现视频水印功能最近接到领导的任务:要求实现视频加水印,原本考虑使用javacv实现每一帧抓取在进行帧运算等 *** 作,后来尝试了,太麻烦果断采用java的Process类去驱动ffmpeg程序。于是先打算在windows本机环境下做一个demo,然后再想法改成生产环境能够使用。但是遇到了一大堆坑。下面是本人做的总结。希望能对新手同志们有所帮助。
1.安装ffmpeg并且在网上找到有关调用代码ffmpeg在windows下安装较简单,直接在网上下载压缩包后解压到指定路径即可。
这里我解压到了D:Program Filesffmpeg-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(); } } }
这是参考:
https://www.cnblogs.com/findingsea/archive/2013/03/14/2959634.html
这位博主在遇到坑的时候都写了,还比较全面。不过我直接调用他的代码还是无法实现视频加水印的问题。
遇到的常见问题:写路径的时候采用:
写法在写ffmpeg路径、输入的原视频路径、输出的加了水印的视频路径,这三个地方正常写没有问题,因为他是识别java调用的方法去驱动的程序的位置。但是在写logo(即要加的水印的位置)的时候不能按照原来的写路径。因为在在ffmpeg命令中它是在双引号里的,ffmpeg的movie filter读取规则是按照linux来的,不支持对盘符(C:、D:、E:)这些的读取。可参考:
https://blog.csdn.net/a373794359/article/details/97496242
如果按这样会报错:
所以吧logo路径改成:
public final static String logo = "E\:/视频/12.png";然后还有一种最常见的错误就是路径写错
比如空格少了造成连读等因素。这个可以自己排查。还有就是文件读取权限等,这种情况比较少见,反正我是没遇到过。当然第一种情况也会造成“系统找不到指定的文件”这样的报错。
程序做一下封装就成了下面的样子参考了:https://www.cnblogs.com/xisita0706/p/12580226.html
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(Mapdto) { 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(); HashMapdto=new HashMap (); 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的路径。
可参考下:
https://www.jianshu.com/p/0cadd56aa9b7?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)