接 视频分割项目预研
http://zhuyufufu.iteye.com/blog/2078404
对于没法用ffmpeg处理的rm等格式,使用了
mplayer的
mencoder来处理。
我的项目重点在视频分割。在此只写rmvb切分的代码。对于别的
ffmpeg无法处理的格式,
mencoder应该都能处理。
下面贴出暂时使用的视频处理工具类,有获
取视频信息、截取视频、获取截图、加水印、视频转换等功能。
其原理都很简单:
使用java调用外部组件对视频进行处理。
后面要添加的功能:
解析组件输出流,生成视频处理进度及结果。
下面贴上代码,方便以后查阅
properties配置文件
class="java" name="code">
#视频切割配置参数
ffmpeg_home=D:/ffmpeg/ffmpeg-20140611-git-b2fb65c-win64-static/
ffmpeg=D:/ffmpeg/ffmpeg-20140611-git-b2fb65c-win64-static/bin/ffmpeg.exe
mplayer=D:/ffmpeg/MPlayer-generic-r37220+gd4be3a8/mplayer.exe
mencoder=D:/ffmpeg/MPlayer-generic-r37220+gd4be3a8/mencoder.exe
package com.zas.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
/**
* 视频操作类
* @author zas
*
*/
public class VideoUtil {
static Logger logger = Logger.getLogger(VideoUtil.class);
final static String FFMPEG = PropertyToolkits.getProperty("ffmpeg");
final static String MENCODER = PropertyToolkits.getProperty("mencoder");
final static String MPLAYER = PropertyToolkits.getProperty("mplayer");
/**
* 获取一个指定视频的基本信息
* @param inputVideoFile
* @return 视频信息
*/
public static String getVideoInfo(String inputVideoFile){
//检查是否能够处理
if(!checkVideoFile(inputVideoFile)){
throw new RuntimeException("文件有误");
}
List<String> commandList = new ArrayList<String>();
commandList.add(FFMPEG);
commandList.add("-i");
commandList.add(inputVideoFile);
// mplayer -identify $inputFileName -nosound -vc dummy -vo null
// List<String> commandList = new ArrayList<String>();
// commandList.add(MPLAYER);
// commandList.add("-identify");
// commandList.add(inputVideoFile);
// commandList.add("-nosound");
// commandList.add("-vc");
// commandList.add("dummy");
// commandList.add("-vo");
// commandList.add("null");
//获取视频信息
String videoInfo = process(commandList);
return videoInfo;
}
/**
* 获取视频截图
* @param inputVideoFile 要处理的视频
* @param imgPath 要截取的截图
* @param parameterMap 参数
* @return
*/
public static String getVideoSnapshots(String inputVideoFile, String imgPath, Map<String, String> parameterMap){
//ffmpeg -i 1111.wma -y -ss 00:00:09 -t 00:00:10 -s 320*240 -f mjpeg -vframes 10 1111_1.jpg
//获取图片的第一帧 ffmpeg commandLine: ffmpeg -y -i 1111.wma -vframes 1 -r 1 -ac 1 -ab 2 -s 320x240 -f image2 1111_1.jpg
//把视频的前30帧转换成一个Animated Gif :
//ffmpeg -i 1111.wma -vframes 30 -y -f gif 1111.gif
//图片时间截取也很重要,很有可能是无效图片或者是黑屏
//建议 增加关键帧,通常第一帧为关键帧,可以使用:vframes:帧参数,舍弃微秒参数,只保留时间参数
//检查是否能够处理
if(!checkVideoFile(inputVideoFile)){
throw new RuntimeException("视频文件有误");
}
/*
-i filename 输入文件
-y 覆盖输出文件
-t duration 设置纪录时间 hh:mm:ss[.xxx]格式的记录时间也支持
-ss position 搜索到指定的时间 [-]hh:mm:ss[.xxx]的格式也支持
*/
List<String> commandList = new ArrayList<String>();
commandList.add(FFMPEG);
commandList.add("-i");
commandList.add(inputVideoFile);
commandList.add("-y");
//位置参数太靠后,会影响抓图效率
commandList.add("-ss");
commandList.add("00:00:09");
// commandList.add("-t");
// commandList.add("00:00:01");
commandList.add("-vframes");
commandList.add("1");
// commandList.add("-r");
// commandList.add("1");
// commandList.add("-ac");
// commandList.add("1");
// commandList.add("-ab");
// commandList.add("2");
// commandList.add("-s");
// commandList.add("320*240");
commandList.add("-f");
commandList.add("mjpeg");
commandList.add(imgPath);
String processInfo = process(commandList);
return processInfo;
}
/**
* 使用ffmpeg 从指定时间开始截取特定时长视频
* @param fromTime 开始时间 00:5:28
* @param inputVideoFile 要截取的视频
* @param duration 要截取的视频时长 00:03:25
* @param outputFile 截取的视频的输出位置
* @param parameterMap 参数
* @return 处理信息
*/
public static String cutting(String fromTime, String inputVideoFile, String duration, String outputFile, Map<String, String> parameterMap){
//检查是否能够处理
if(!checkVideoFile(inputVideoFile)){
throw new RuntimeException("视频文件有误");
}
File file = new File(outputFile);
if(file.exists()){
System.out.println("outputFile exists !");
return "outputFile exists!";
}
String filetype = inputVideoFile.substring(inputVideoFile.lastIndexOf(".") + 1);
if("rm".equalsIgnoreCase(filetype) || "rmvb".equalsIgnoreCase(filetype)){
return cuttingRmvb(fromTime, inputVideoFile, duration, outputFile, parameterMap);
}
//ffmpeg -ss 00:5:28 -i "1111.wmv" -acodec copy -vcodec copy -t 00:03:25 output.wmv
//这行命令解释为:从文件 1111.wmv 第 5:28 分秒开始,截取 03: 25 的时间,其中视频和音频解码不变,输出文件名为 output.wmv 。
List<String> commandList = new ArrayList<String>();
commandList.add(FFMPEG);
commandList.add("-ss");
commandList.add(fromTime);
commandList.add("-i");
commandList.add(inputVideoFile);
commandList.add("-acodec");
commandList.add("copy");
commandList.add("-vcodec");
commandList.add("copy");
commandList.add("-t");
commandList.add(duration);
commandList.add(outputFile);
String processInfo = process(commandList);
return processInfo;
}
/**
* 使用ffmpeg 从指定时间开始截取特定时长视频
* @param fromTime 开始时间 00:5:28
* @param inputVideoFile 要截取的视频
* @param duration 要截取的视频时长 00:03:25
* @param outputFile 截取的视频的输出位置
* @param parameterMap 参数
* @return 处理信息
*/
public static String cuttingRmvb(String fromTime, String inputVideoFile, String duration, String outputFile, Map<String, String> parameterMap){
//检查是否能够处理
if(!checkVideoFile(inputVideoFile)){
throw new RuntimeException("视频文件有误");
}
// mencoder basket.rm -ovc lavc -oac mp3lame -o basket.avi -ss 5:00 -endpos 8:00
List<String> commandList = new ArrayList<String>();
commandList.add(MENCODER);
commandList.add(inputVideoFile);
commandList.add("-ovc");
commandList.add("lavc");
commandList.add("-oac");
commandList.add("mp3lame");
commandList.add("-ss");
commandList.add(fromTime);
commandList.add("-endpos");
commandList.add(duration);
commandList.add("-o");
commandList.add(outputFile);
String processInfo = process(commandList);
return processInfo;
}
/**
* 视频转换
* @param inputVideoFile 视频文件
* @param outputFile 转换后的视频文件
* @param parameterMap 其余参数
* @return
*/
public static String videoConverter(String inputVideoFile, String outputFile, Map<String, String> parameterMap){
//检查是否能够处理
if(!checkVideoFile(inputVideoFile)){
throw new RuntimeException("视频文件有误");
}
//视频的格式有很多,以mp4和flv为例子
// ffmpeg -i test.mp4 -ab 56 -ar 22050 -qmin 2 -qmax 16 -b 320k -r 15 -s 320x240 outputfile.flv //mp4 转 flv
// ffmpeg -i outputfile.flv -copyts -strict -2 test.mp4 //flv 转 mp4
List<String> commandList = new ArrayList<String>();
commandList.add(FFMPEG);
commandList.add("-y");
commandList.add("-i");
commandList.add(inputVideoFile);
commandList.add("-ab");
commandList.add("5600000");
commandList.add("-ar");
commandList.add("22050");
commandList.add("-b");
commandList.add("500");
commandList.add("-s");
commandList.add("320*240");
commandList.add(outputFile);
String processInfo = process(commandList);
return processInfo;
}
/**
* 视频加水印
* @param inputVideoFile 要处理的视频文件
* @param outputFile 输出的视频文件
* @param parameterMap 参数
* @return
*/
public static String addWatermark(String inputVideoFile, String outputFile, Map<String, String> parameterMap){
//检查是否能够处理
if(!checkVideoFile(inputVideoFile)){
throw new RuntimeException("视频文件有误");
}
//http://blog.51yip.com/linux/1584.html
//#ffmpeg -y -i test.mp4 -acodec copy -vf "movie=logo.jpg [logo]; [in][logo] overlay=10:10:1 [out]" test2.mp4
//overlay=10:10:1,后三个数据表示是距离左边的距离,距离上边的距离,是否透明,1表示透明。上例我用的是jpg,当然不可能透明。
//# ffmpeg -y -i test.mp4 -acodec copy -vf "movie=uwsgi.jpg [logo]; [in][logo] overlay=enable='lte(t,1)' [out]" test2.mp4
//overlay=enable='lte(t,1)' ,这个参数表示,水印在前一秒显示。
//ffmpeg -y -i D:/ffmpeg/video/w.mkv -acodec copy -t 00:01:10 -vf "movie=logo.png [logo]; [in][logo] overlay=10:10:1 [out]" D:/ffmpeg/video/w_logo.mkv
List<String> commandList = new ArrayList<String>();
commandList.add(FFMPEG);
commandList.add("-i");
commandList.add(inputVideoFile);
commandList.add("-y");
commandList.add("-acodec");
commandList.add("copy");
commandList.add("-vf");
commandList.add("\"movie=logo.png [logo]; [in][logo] overlay=10:10:1 [out]\"");
commandList.add(outputFile);
//
// String command = FFMPEG + " -y -i \"D:/ffmpeg/video/a b/w.mkv\" -acodec copy -t 00:01:10 -vf \"movie=logo.png [logo]; [in][logo] overlay=10:10:1 [out]\" D:/ffmpeg/video/w_logo.mkv";
// String processInfo = exec(command);
String processInfo = process(commandList);
return processInfo;
}
/**
* 命令执行
* @param command
* @return
*/
public static String exec(String command) {
long beginTime = System.nanoTime();
Runtime rt = Runtime.getRuntime();
try {
Process process = rt.exec(command);
/* StringTokenizer st = new StringTokenizer(command);
String[] cmd = new String[st.countTokens()];
for (int i = 0; st.hasMoreTokens(); i++){
cmd[i] = st.nextToken();
System.out.println(cmd[i]);
}
cmd[0] = new File(cmd[0]).getPath();
StringBuilder cmdbuf = new StringBuilder(80);
for (int i = 0; i < cmd.length; i++) {
if (i > 0) {
cmdbuf.append(' ');
}
String s = cmd[i];
if (s.indexOf(' ') >= 0 || s.indexOf('\t') >= 0) {
if (s.charAt(0) != '"') {
cmdbuf.append('"');
cmdbuf.append(s);
if (s.endsWith("\\")) {
cmdbuf.append("\\");
}
cmdbuf.append('"');
} else if (s.endsWith("\"")) {
The argument has already been quoted.
cmdbuf.append(s);
} else {
Unmatched quote for the argument.
throw new IllegalArgumentException();
}
} else {
cmdbuf.append(s);
}
}
String cmdstr = cmdbuf.toString();
System.out.println("cmdstr : " + cmdstr);*/
final InputStream isNormal = process.getInputStream();
new Thread(new Runnable() {
public void run() {
BufferedReader br = new BufferedReader(new InputStreamReader(isNormal));
StringBuilder buf = new StringBuilder();
String line = null;
try {
while((line = br.readLine()) != null){
buf.append(line + "\n");
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("输出结果为:" + buf);
}
}).start(); // 启动单独的线程来清空process.getInputStream()的缓冲区
InputStream isError = process.getErrorStream();
BufferedReader br2 = new BufferedReader(new InputStreamReader(isError));
StringBuilder buf = new StringBuilder();
String line = null;
while((line = br2.readLine()) != null){
buf.append(line + "\n");
}
System.out.println("错误输出结果为:" + buf);
try {
process.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
long endTime = System.nanoTime();
System.out.println("视频处理耗时: " + (endTime - beginTime) / 1000000 + " 毫秒 ");
return null;
}
/**
* 根据命令处理视频
* @param commandList
* @param processInfo
* @return 处理信息
*/
private static String process(List<String> commandList) {
StringBuffer processInfo = new StringBuffer();
ProcessBuilder builder = new ProcessBuilder();
builder.command(commandList);
builder.redirectErrorStream(true);
long beginTime = System.nanoTime();
try {
Process p = builder.start();
//保存ffmpeg的输出结果流
BufferedReader buf = null;
buf = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
while ((line = buf.readLine()) != null) {
System.out.println(line);
processInfo.append(line + "\n");
}
p.waitFor();// 这里线程阻塞,将等待外部转换进程运行成功运行结束后,才往下执行
} catch (IOException e) {
e.printStackTrace();
logger.error(e);
throw new RuntimeException("视频处理出错");
} catch (InterruptedException e) {
e.printStackTrace();
logger.error(e);
throw new RuntimeException("视频处理出错");
}
long endTime = System.nanoTime();
System.out.println("处理耗时: " + (endTime - beginTime) / 1000000 + " 毫秒。 ");
System.out.println("视频处理结果信息: \n" + processInfo);
return processInfo.toString();
}
/**
* 检测视频是否能够被处理
* @param videoPath
* @return
*/
private static boolean checkVideoFile(String videoPath) {
if(null == videoPath){
return false;
}
//根据后缀做类型检测
//检查是否文件以及文件是否存在
File videoFile = new File(videoPath);
if(!videoFile.isFile() || !videoFile.exists()){
return false;
}
return true;
}
public static void main(String[] args) {
// String inputVideoFile = "D:/ffmpeg/video/【天下足球网www.txzqw.com】下半场.rmvb";
String outputFile = "D:/ffmpeg/video/【天下足球网www.txzqw.com】下半场_1.rmvb";
String inputVideoFile = "D:/ffmpeg/video/a b/w.mkv";
// String outputFile = "D:/ffmpeg/video/w_1.mkv";
// String inputVideoFile = "D:/ffmpeg/video/Wildlife.wmv";
// String outputFile = "D:/ffmpeg/video/Wildlife.wmv.flv";
String fromTime = "00:00:00";
String duration = "00:3:28";
VideoUtil.getVideoInfo(inputVideoFile);
// VideoUtil.cuttingRmvb(fromTime, inputVideoFile, duration, outputFile, null);
// String imgPath = "D:/ffmpeg/video/1111.jpg";
// VideoUtil.getVideoSnapshots(inputVideoFile, imgPath, null);
// VideoUtil.addWatermark(inputVideoFile, outputFile, null);
// VideoUtil.videoConverter(inputVideoFile, outputFile, null);
// VideoUtil.getVideoInfo(inputVideoFile);
}
}