java 做视频转换主要用到开源的ffmpeg或者mencoder,还要有MP4Box。
注:由于平时都没有时间写博客,所以思路我就不写了,有问题问我,不一定马上回复。
详细介绍:
ffmpeg:http://www.ffmpeg.org/
mencoder:http://en.wikipedia.org/wiki/MEncoder
MP4Box:http://gpac.wp.mines-telecom.fr/mp4box/mp4box-documentation/
主要实现:
1.获取视频元数据信息
2.视频相互转换
3.视频加文字及图片水印
4.视频截图
思路:
配置文件中定义各转换工具的路径:(相关工具去官网下载)
<!-- ################配置系统用到的转换工具相关参数 开始....--> <!-- ##ffmpeg.so文件路径 --> <param name="ffmpegPath" value="D:\conver\ffmpeg-win.exe" /> <!-- ##memcoder.so文件路径 --> <param name="mencoderPath" value="D:\conver\mencoder.exe" /> <!-- ##给视频加水印的shell脚本路径 --> <param name="shellPath" value="D:\conver\coverVideo.bat" /> <!-- ##定义MP4Box工具路径 --> <param name="mp4BoxPath" value="D:\conver\MP4Box" /> <!-- ##定义图片转换工具路径 --> <param name="imgConverPath" value="C:/Program Files/ImageMagick-6.3.9-Q16/convert.exe" /> <!-- ################配置系统用到的转换工具相关参数 结束-->
2.获取视频音频的各项属性帮助类
import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.List; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.apache.oro.text.regex.MalformedPatternException; import org.apache.oro.text.regex.MatchResult; import org.apache.oro.text.regex.Pattern; import org.apache.oro.text.regex.PatternCompiler; import org.apache.oro.text.regex.PatternMatcher; import org.apache.oro.text.regex.Perl5Compiler; import org.apache.oro.text.regex.Perl5Matcher; import com.brainsoon.bsrcm.common.utils.BaseCommonUtil; import com.brainsoon.bsrcm.system.support.CacConver; /** * 获取视频音频的各项属性帮助类 * 如果需要修改或者添加属性,只要扩展下面的二维数组和修改下面getVideoInfo()方法 * @author tanghui * */ public class VideoInfoHelps { protected static final Logger logger = Logger.getLogger(VideoInfoHelps.class); public static final String ffmpegPath; // ffmpeg.exe的目录 static{ ffmpegPath = BaseCommonUtil.getProRealPath("ffmpegPath"); } /** * 根据属性获取视频信息 * @param videoPath * @return */ public static VideoInfo getVideoInfo(String videoPath) { VideoInfo videoInfo = new VideoInfo(); if(CacConver.exitFile(videoPath)){ String videoType = videoPath.substring(videoPath.lastIndexOf(".")+1, videoPath.length()); if(CacConver.isNeedVideoFormat(videoType)){ String result = processVideo(videoPath); if(StringUtils.isNotEmpty(result)){ PatternCompiler compiler =new Perl5Compiler(); try { String regexDuration ="Duration: (.*?), start: (.*?), bitrate: (\\d*) kb\\/s"; String regexVideo ="Video: (.*?), (.*?), (.*?)[,\\s]"; String regexAudio ="Audio: (\\w*), (\\d*) Hz"; Pattern patternDuration = compiler.compile(regexDuration,Perl5Compiler.CASE_INSENSITIVE_MASK); PatternMatcher matcherDuration = new Perl5Matcher(); if(matcherDuration.contains(result, patternDuration)){ MatchResult re = matcherDuration.getMatch(); videoInfo.setPlayingAllTime( re.group(1)); videoInfo.setPlayingStartTime( re.group(2)); videoInfo.setBitrateSize( re.group(3)); } Pattern patternVideo = compiler.compile(regexVideo,Perl5Compiler.CASE_INSENSITIVE_MASK); PatternMatcher matcherVideo = new Perl5Matcher(); if(matcherVideo.contains(result, patternVideo)){ MatchResult re = matcherVideo.getMatch(); videoInfo.setCodeFormat( re.group(1)); videoInfo.setVideoFormat( re.group(2)); videoInfo.setResolution( re.group(3)); } Pattern patternAudio = compiler.compile(regexAudio,Perl5Compiler.CASE_INSENSITIVE_MASK); PatternMatcher matcherAudio = new Perl5Matcher(); if(matcherAudio.contains(result, patternAudio)){ MatchResult re = matcherAudio.getMatch(); videoInfo.setAudioCoding(re.group(1)); videoInfo.setAudioFrequency( re.group(2)); } } catch (MalformedPatternException e) { logger.error("获取【" + videoPath +"】视频信息失败!"); } logger.info("获取【" + videoPath +"】视频信息成功!"); }else{ logger.info("执行成功!但未获取到【" + videoPath +"】视频信息!"); } }else{ logger.debug("【" + videoPath + "】文件格式不支持!"); } } return videoInfo ; } /** * ffmpeg能解析的格式:(asx,asf,mpg,wmv,3gp,mp4,mov,avi,flv等) * @param inputPath * @return */ private static String processVideo(String filePath) { List<String> commend=new java.util.ArrayList<String>(); commend.add(ffmpegPath);//可以设置环境变量从而省去这行 commend.add("-i"); commend.add(filePath); try { ProcessBuilder builder = new ProcessBuilder(); builder.command(commend); builder.redirectErrorStream(true); Process p= builder.start(); BufferedReader buf = null; // 保存ffmpeg的输出结果流 String line = null; buf = new BufferedReader(new InputStreamReader(p.getInputStream())); StringBuffer sb= new StringBuffer(); while ((line = buf.readLine()) != null) { sb.append(line); continue; } p.waitFor();//这里线程阻塞,将等待外部转换进程运行成功运行结束后,才往下执行 return sb.toString(); } catch (Exception e) { logger.error("ffmpeg解析视频文件【" + filePath +"】失败!"); return null; } } }
3.其他的工具类
/** * 等待进程处理 * @param p * @return */ @SuppressWarnings("unused") public static int doWaitFor(Process p) { InputStream in = null; InputStream err = null; int exitValue = -1; // returned to caller when p is finished try { in = p.getInputStream(); err = p.getErrorStream(); boolean finished = false; // Set to true when p is finished while (!finished) { try { while (in.available() > 0) { Character c = new Character((char) in.read()); } while (err.available() > 0) { Character c = new Character((char) err.read()); } exitValue = p.exitValue(); finished = true; } catch (IllegalThreadStateException e) { Thread.currentThread(); Thread.sleep(500); } } } catch (Exception e) { logger.error("doWaitFor();: unexpected exception - " + e.getMessage()); } finally { try { if (in != null) { in.close(); } } catch (IOException e) { logger.error("等待进程处理错误"); } if (err != null) { try { err.close(); } catch (IOException e) { logger.error("等待进程处理错误"); } } } return exitValue; } /** * 删除文件 * @param filepath */ public static void deleteFile(String filepath) { File file = new File(filepath); if (file.exists()) { if (file.delete()) { logger.info("文件【" + filepath + "】已删除"); } } } /** * 根据时间返回总秒数 形如:(00:12:12) * @param timeStr * @return */ public static String getSplitStr(String timeStr){ String secStr="0";//返回秒 if(timeStr != null && !timeStr.equals("") ){ String[] str = timeStr.split(":"); int subInt0 = Integer.parseInt(str[0]); int subInt1 = Integer.parseInt(str[1]); String str2s = ""; if(str[2].length() >2&&str[2].indexOf(".")>0){ str2s = str[2].substring(0,str[2].indexOf(".")); }else{ str2s = str[2]; } int subInt2 = Integer.parseInt(str2s); Long countNum = subInt0 * 3600L + subInt1 * 60 + subInt2; secStr = countNum + ""; } return secStr; } /** * 计算两个字符串时间相减 如:("00:22:22") * * @param time1 * @param time2 * @return */ public static String calTime(String time1, String time2){ Long time1Long = Long.parseLong(time1); Long time2Long = Long.parseLong(time2); Long timeLong = time2Long-time1Long; StringBuffer sbuffer = null; if(timeLong > 0){ int hour = (int) (timeLong/3600); int minute = (int) ((timeLong-hour*3600)/60); int second =(int) ((timeLong-hour*3600-minute*60)%60); sbuffer = new StringBuffer(); if(hour<10){ sbuffer.append("0"); } sbuffer.append(Integer.toString(hour)); sbuffer.append(":"); if(minute<10){ sbuffer.append("0"); } sbuffer.append(Integer.toString(minute)); sbuffer.append(":"); if(second<10){ sbuffer.append("0"); } sbuffer.append(Integer.toString(second)); return sbuffer.toString(); }else{ logger.error("时间不能为负数!可能原因是传入的时间位置不对!"); return ""; } }