`
zhuyufufu
  • 浏览: 137303 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

视频操作类

阅读更多
接 视频分割项目预研
http://zhuyufufu.iteye.com/blog/2078404

   对于没法用ffmpeg处理的rm等格式,使用了mplayermencoder来处理。

   我的项目重点在视频分割。在此只写rmvb切分的代码。对于别的ffmpeg无法处理的格式,mencoder应该都能处理。

   下面贴出暂时使用的视频处理工具类,有获取视频信息、截取视频、获取截图、加水印、视频转换等功能。

   其原理都很简单:使用java调用外部组件对视频进行处理

   后面要添加的功能:解析组件输出流,生成视频处理进度及结果

下面贴上代码,方便以后查阅

properties配置文件
#视频切割配置参数
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);
	}

}

  
分享到:
评论

相关推荐

    视频压缩软件,包含C#视频操作类

    摄像头操作的基本类。 对摄像头摄像及保存视频,压缩视频 希望对需要摄像头视频压缩的朋友有帮助。 缺点是压缩速度太慢, 不知哪位大虾有没有好的办法分享一下。

    c#+库文件XML操作类时间操作类视频转换类文件操作类FTP操作类弹出信息类等库文件.zip

    1. **XML操作类**: XML(eXtensible Markup Language)是一种结构化数据存储格式,常用于数据交换和配置文件。C#中的System.Xml命名空间提供了处理XML文档的类,如XmlDocument、XmlNode、XmlElement等。这些类可以...

    AVI操作类,图片转视频,视频截图

    在本文中,我们将深入探讨如何使用AVI操作类来处理视频,特别是如何将图片转换成AVI格式的视频,以及如何从视频中截取图片。 首先,我们需要理解AVI操作类的基本概念。这类类库通常提供了一系列接口和方法,用于...

    外部控件操作类.rar

    "外部控件操作类"可能是指一个包含了一系列方法的编程类库,用于简化这些操作。 在Windows操作系统中,COM(Component Object Model)组件和ActiveX控件是常见的外部控件技术。COM允许开发人员创建可重用的代码组件...

    最新C#类库典藏版源码 包含有FTP操作类、导出Excel、文件操作类、弹出消息类等.zip

    FTP操作类、导出Excel、配置文件操作类、 文件操作类、弹出消息类、XML操作类、 弹出消息类、分词辅助类、时间操作类、 汉字转拼音、压缩解压缩、条形码、 正则表达式、日历、上传下载、 视频转换类、随机数类、条形...

    汉字转拼音、日历、上传下载、时间操作类、视频转换类

    4. 时间操作类:时间操作类主要用于时间的增减、比较、格式化等。在Python中,有`datetime`模块提供了一系列类和函数,如`datetime`, `timedelta`等,可以方便地进行时间的运算和格式化输出。对于复杂的时区处理,...

    视频裁剪工具类

    总之,"视频裁剪工具类"是针对移动设备用户需求而设计的,旨在提供便捷、高效的视频编辑功能,通过裁剪和合并操作,帮助用户创作出符合自己需求的个性化视频内容。在实际开发中,需要关注各种格式的兼容性、用户体验...

    计算机前端-核心编程.视频03单例的数据库操作类.avi

    计算机前端-核心编程.视频03单例的数据库操作类.avi

    C#200个基础工具类大全.zip

    Excel操作类;FTP操作类;Html操作类;IP辅助类;JSON操作;JS操作;URL的操作类;XML操作类;处理多媒体的公共类;弹出消息类;二维码操作类;汉字转拼音;加密解密;科学计数,数学;类型转换;配置文件操作类;上传下载;时间操作...

    封装好的Android视频传输surfaceView类

    在Android开发中,SurfaceView是一种常用的视图组件,它提供了硬件加速的能力,常用于处理复杂的图形操作,如视频播放和游戏画面渲染。本项目提供的"封装好的Android视频传输SurfaceView类"是针对视频数据处理的一个...

    winform第三章封装数据库操作类(无视频).

    在本主题中,我们将深入探讨如何在C#中封装数据库操作类,特别是在Windows Forms(WinForms)应用程序中。本教程的焦点是利用SQL Server 2005进行数据交互,而没有依赖视频教程。 首先,我们需要理解封装的概念。...

    winform第三章封装数据库操作类(有视频)

    本教程主要聚焦于在Winform应用中封装数据库操作类,通过C#编程语言实现。数据库封装是为了提高代码的可重用性、可维护性和降低耦合度,使程序更加模块化。以下将详细介绍这一主题: 首先,我们需要理解什么是...

    c#帮助类文件包含常用的基础C#类

    二维码操作类,分词辅助类,分页,各种验证帮助类,计划任务,缓存,加密解密,配置文件操作类,日历,日志,上传下载,时间操作类,视频帮助类,条形码帮助类,图片操作类,网站路径操作类,文件操作类,压缩解压缩...

    C# 视频转换类

    该代码中含有C# 视频转换所使用的的基础操作类,可直接使用

    PHP_POP3操作类

    PHP_POP3操作类是一个用于处理POP3协议的PHP类库,它允许开发人员通过编程方式与邮件服务器进行交互,接收和处理电子邮件。POP3(Post Office Protocol version 3)是互联网上广泛使用的邮件检索协议,它使用户可以...

    视频下载工具类

    对于开发者来说,理解和实现这样的工具类不仅有助于提升用户体验,还能深入学习多线程编程、网络通信和文件操作等相关技术。而对于普通用户而言,这样的工具则提供了更高效、更灵活的视频下载体验。

    公共封装类大全(各种基类)

    Jquery相关、jQuery.cookie帮助类、访问系统相关、C#基础类库(Chart图形、CSV文件转换、FTP操作类、加密/解密帮助类、FTP操作类、JS操作类、...操作类、视频转换类、条形码、文件操作类、验证码)等等还有好多就没写了

    FME常用转换器之字符串操作类视频讲解,讲解如何使用FME对字符串文件进行操作处理

    1.FME常用转换器之字符串操作类视频讲解,讲解如何使用FME对字符串文件进行操作处理。 2.本视频讲解中使用的FME版本为2020.2中文版。 3.本资源仅为视频讲解,用户需自行安装相应版本的FME。

    视频剪辑类APP.pdf

    视频剪辑类APP.pdf 视频剪辑类APP.pdf是关于视频剪辑类APP的应用开发指南,旨在帮助开发者快速构建功能强大、界面美观的视频剪辑应用程序。下面是该资源的详细知识点解读: APP 应用开发 视频剪辑类APP.pdf中提供...

Global site tag (gtag.js) - Google Analytics