web/java实现多种格式视频上传、转码、截图、播放、下载等功能附源码(详细)

web /java 实现多种格式视频上传、转码、播放、下载

1.前言

前段时间一直在做一个生物资源共享平台,采用SSM框架技术,其中涉及一个模块,是关于视频资源的播放。

本来不是很大的问题,但是无奈用户要求较多,需要支持N种格式的视频播放,没有办法,转码是必不可少的了。
对于一个从来没接触过这些什么音频视频编码格式的我,浪费了很长时间,其中很多博客的教学都不太完善。

反正就是有一堆麻烦事情,坑也多,所以我还是记录一下,以后需要的时候就不要那么麻烦了。

采用工具:

ffmpeg 软件
Mencoder 软件
Video.js 文件

废话不说了,直接上干货。

2.源码

关于上传下载就是不做重点讲了,反正代码贴出来了,直接用就行。

jsp上传页面:
add_video.jsp
实际上就是一个form表单提交请求到
action=“back/addVideo” encType=“multipart/form-data” method=“post”

源码如下:

 <div id="tab1" class="tabson">
            <div class="formtext">Hi,<b>${user.username}</b>,欢迎您使用教学资源共享平台!</div>
            <form id="frm2" name="frm2" action="back/addVideo" encType="multipart/form-data" method="post">
	           <ul class="forminfo">
	               <li>
	                   <label>资源名称<b>*</b></label><input id="title" name="title" type="text" class="dfinput" value=""  style="width:518px;"/>
	                    <input id="uid" name="uid" type="text"  hidden="hidden" value="${uid}"/>
	               </li>
	                  <li><label>审核状态<b>*</b></label>
	                  	   <div class="vocation">
							  	<select class="select1" id="state" name="state">
							    <option value="0">未审核</option>
							    <option value="1">通过</option>
							    <option value="2">不通过</option>
							    <option value="3">下架</option>
							    </select>
						  </div>
	                  </li>
	                  <li><label>上传者<b>*</b></label><input id="writer" name="writer" type="text" class="dfinput" value="${user.username}"  style="width:518px;"/></li>
	                  <li><label>上传时间<b>*</b></label><input id="time" name="time" type="date" class="dfinput" value=""  style="width:518px;"  /></li>
	                  <li><label>所属级别<b>*</b></label>
	                  	   <div class="vocation">
							  	<select class="select1" id="grade" name="grade">
							    <option value="0">初中</option>
							    <option value="1">高中</option>
							    </select>
						  </div>
					  </li>
					  <li><label>资源类型<b>*</b></label>
						  <div class="vocation">
							  	<select class="select1" id="subclass" name="subclass">
							    <!-- 邹涛20180814 -->
							    <c:if test="${uid eq 3}">
							    	<option value="微视频">微视频</option>
							    	<option value="实验视频">实验视频</option>
							    	<option value="教学视频">教学视频</option>
							    </c:if>
							    </select>
						  </div>
					  </li>
					  <li><label>上传资源<b>*</b></label><input id="file" name="file" type="file" style="width:518px;"  /></li>
					  <li><label>点击量<b>*</b></label><input id="clicks" name="clicks" type="text" class="dfinput" value="0"  style="width:518px;"/></li>	               
	               <li>
	               		 <label>&nbsp;</label><input name="" type="button" class="btn" onclick="startPost();" value="提交"/>
	               </li>
	           </ul>
            </form>
        </div>
    </div>

addVideo控制层

源码如下:

					/**
					 * @Description:(视频资源的单独上传的接收)
					 * @param:@param request
					 * @param:@param response
					 * @param:@param session
					 * @param:@return
					 * @return:ModelAndView
					 * @author:Zoutao
					 * @date:2018-6-12
					 * @version:V1.0
					 */
					@RequestMapping(value = "/addVideo")
					public ModelAndView uploadflie_Video(
							@RequestParam("file") CommonsMultipartFile file,
							HttpServletRequest req, HttpServletRequest request) {
						System.out.println("进入addVideo视频上传控制层");
						
						if (file.getSize() != 0) {
							//上传的多格式的视频文件-作为临时路径保存,转码以后删除-路径不能写//
							String path = "E:/Projectpicture/websiteimages/temp/";
							
							File TempFile = new File(path);
							if (TempFile.exists()) {
								if (TempFile.isDirectory()) {
									System.out.println("该文件夹存在。");
								}else {
									 System.out.println("同名的文件存在,不能创建文件夹。");
								}
							}else {
								 System.out.println("文件夹不存在,创建该文件夹。");
								 TempFile.mkdir();
							}
							
							// 获取上传时候的文件名
							String filename = file.getOriginalFilename();
							
							// 获取文件后缀名
							String filename_extension = filename.substring(filename
									.lastIndexOf(".") + 1);
							System.out.println("视频的后缀名:"+filename_extension);
							
							//时间戳做新的文件名,避免中文乱码-重新生成filename
							long filename1 = new Date().getTime();
							filename = Long.toString(filename1)+"."+filename_extension;
							
							//去掉后缀的文件名
							String filename2 = filename.substring(0, filename.lastIndexOf("."));
							System.out.println("视频名为:"+filename2);
							
							//源视频地址+重命名后的视频名+视频后缀
							String yuanPATH =(path+filename);  
							
							System.out.println("视频的完整文件名1:"+filename);
							System.out.println("源视频路径为:"+yuanPATH);
							
							//上传到本地磁盘/服务器
							try {
								System.out.println("写入本地磁盘/服务器");
								InputStream is = file.getInputStream();
								OutputStream os = new FileOutputStream(new File(path, filename));
								int len = 0;
								byte[] buffer = new byte[2048];
								
								while ((len = is.read(buffer)) != -1) {
									os.write(buffer, 0, len);
								}
								os.close();
								os.flush();
								is.close();
							} catch (FileNotFoundException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							} catch (IOException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
							
							System.out.println("========上传完成,开始调用转码工具类=======");
							//调用转码机制flv mp4 f4v m3u8 webm ogg放行直接播放,
							//asx,asf,mpg,wmv,3gp,mov,avi,wmv9,rm,rmvb等进行其他转码为mp4
							
							if (filename_extension.equals("avi") || filename_extension.equals("rm") 
								|| filename_extension.equals("rmvb") || filename_extension.equals("wmv")
								|| filename_extension.equals("3gp")  || filename_extension.equals("mov")
								||filename_extension.equals("flv")   || filename_extension.equals("ogg")
								
								) {
								
								ConverVideoTest c = new ConverVideoTest();
						        c.run(yuanPATH);   //调用转码
								System.out.println("=================转码过程彻底结束=====================");
							}
							
							//获取转码后的mp4文件名
							String Mp4path = "E://Projectpicture/websiteimages/finshvideo/";
							filename2 = filename2+".mp4";
							String NewVideopath =Mp4path +filename2;
							System.out.println("新视频的url:"+NewVideopath);
							
							//删除临时文件
							File file2 = new File(path);
							 if (!file2.exists()) {
						       System.out.println("没有该文件");
						       }
						     if (!file2.isDirectory()) {
						    	   System.out.println("没有该文件夹");
						       }
						     String[] tempList = file2.list();
						     File temp = null;
						     for (int i = 0; i < tempList.length; i++) {
						          if (path.endsWith(File.separator)) {
						             temp = new File(path + tempList[i]);
						          } else {
						              temp = new File(path + File.separator + tempList[i]);
						          }
						          if (temp.isFile() || temp.isDirectory()) {
						             temp.delete();		//删除文件夹里面的文件
						          }
						        }
						       System.out.println("所有的临时视频文件删除成功");
							
							// 实例化用户类
							tb_resource resource = new tb_resource();
							
							//获取填写的相关信息
							String title = request.getParameter("title");
							String writer = request.getParameter("writer");
							int state = Integer.parseInt(request.getParameter("state"));
							String time = request.getParameter("time");
							int clicks = Integer.parseInt(request.getParameter("clicks"));
							int grade = Integer.parseInt(request.getParameter("grade"));
							String subclass = request.getParameter("subclass");
							int uid = Integer.parseInt(request.getParameter("uid"));
							
							//数据库存储信息
							resource.setTitle(title);
							resource.setWriter(writer);
							resource.setTime(time);
							resource.setClicks(clicks);
							resource.setGrade(grade);
							resource.setSubclass(subclass);
							resource.setState(state);
							resource.setUid(uid);
							resource.setSuffix(filename2);
							resource.setUrl(NewVideopath); 			//已转码后的视频存放地址
							
							// 实现对数据的更新
							int n = 0;
							n = tb_resourceService.insertResource(resource);
							
							if (n != 0) {
								return new ModelAndView("back/public/success").addObject(
										"notice", "resourceList?uid=" + uid
												+ "&grade=-1&state=-1&subclass=" + subclass);
							} else {
								return new ModelAndView("back/public/fail").addObject("notice",
										"resourceList?uid=" + uid
												+ "&grade=-1&state=-1&subclass=" + subclass);
							}
						}
						return null;
					}

其中,一开始是一个指定上传路径,然后后面是一个自动创建文件夹,防止报错。
从写入本地磁盘/服务器那里,就是采用流来上传文件。上传到指定位置。
然后 c.run(yuanPATH); 就是上传完以后调用转码机制。

各种类型的视频,最后都会统一转换为mp4格式保存下来,然后删除产生的临时文件。

后面的就是对数据的插入sql,以及页面跳转。

转码工具类:
源码如下:
Contants.java

public class Contants {

	/**
	 * @Description:(3.工具类主类)设置转码工具的各个路径
	 * @param:@param args   
	 * @return:void  
	 * @author:Zoutao
	 * @date:2018-6-22
	 * @version:V1.0
	 */
	
	public static final String ffmpegpath = "D:/ffmpeg/bin/ffmpeg.exe";		// ffmpeg工具安装位置
	public static final String mencoderpath = "D:/ffmpeg/bin/mencoder"; 	// mencoder工具安装的位置
	
	public static final String videofolder = "E://Projectpicture/websiteimages/temp/"; 	// 需要被转换格式的视频目录
	public static final String videoRealPath = "E://Projectpicture/websiteimages/temp/"; 	// 需要被截图的视频目录
	
	public static final String targetfolder = "E://Projectpicture/websiteimages/finshvideo/"; // 转码后视频保存的目录
	public static final String imageRealPath = "E://Projectpicture/websiteimages/finshimg/"; // 截图的存放目录
	

}

这是指定源视频上传的位置,临时视频产生的位置,转码以后的位置,视频截图的存放位置,以及两个工具的安装位置。

ConverVideoTest.java
源码如下:

package com.resource.mytools;
public class ConverVideoTest {
	
	/**
	 * @Description:(1.转码和截图功能调用)
	 * @param:@param yuanPATH   
	 * @return:void  
	 * @author:Zoutao
	 * @date:2018-6-23
	 * @version:V1.0
	 */
	
	/*本地测试专用--zoutao*/
	 public static void main(String[] args) {
		ConverVideoTest c = new ConverVideoTest();
		String yuanPATH = "D:/testfile/video/短avi测试.avi";  //本地源视频
        c.run(yuanPATH);
	}
 
	//web调用
    public void run(String yuanPATH) {
        try {
            // 转码和截图功能开始
        	
            //String filePath = "D:/testfile/video/rmTest.rm";  //本地源视频测试
        	
        	String filePath = yuanPATH;				//web传入的源视频
        	System.out.println("ConverVideoTest说:传入工具类的源视频为:"+filePath);
        	
            ConverVideoUtils zout = new ConverVideoUtils(filePath);  //传入path
            String targetExtension = ".mp4";  				//设置转换的格式
            boolean isDelSourseFile = true;
            
            //删除源文件
            boolean beginConver = zout.beginConver(targetExtension,isDelSourseFile);
            System.out.println(beginConver);
 
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这是用来指定转码的视频格式,调用转码工具类。

ConverVideoUtils.java
源码如下:

package com.resource.mytools;
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;

/**
 * 
* @Title: ConverVideoUtils.java
* @Package:com.resource.mytools
* @Description:(2.转码和截图功能)
* @see:接收Contants实体的路径
* @author:Zoutao 
* @date:2018-7-15
* @version:V1.0
 */

public class ConverVideoUtils {
	private String sourceVideoPath;							//源视频路径
	private String filerealname;				 			//文件名不包括后缀名
	private String filename; 								//包括后缀名
	private String videofolder = Contants.videofolder; 		// 别的格式视频的目录
	private String targetfolder = Contants.targetfolder; 	// flv视频的目录
	private String ffmpegpath = Contants.ffmpegpath;		 // ffmpeg.exe的目录
	private String mencoderpath = Contants.mencoderpath; 	// mencoder的目录
	private String imageRealPath = Contants.imageRealPath;   // 截图的存放目录
	
	public ConverVideoUtils() {
	}

	//重构构造方法,传入源视频
	public ConverVideoUtils(String path) {
		sourceVideoPath = path;
	}

	//set和get方法传递path
	public String getPATH() {
		return sourceVideoPath;
	}

	public void setPATH(String path) {
		sourceVideoPath = path;
	}
	
	/**
	 * 转换视频格式
	 * @param String targetExtension 目标视频后缀名 .xxx
	 * @param boolean isDelSourseFile 转换完成后是否删除源文件
	 * @return
	 */
	public boolean beginConver(String targetExtension, boolean isDelSourseFile) {
		File fi = new File(sourceVideoPath);
		
		filename = fi.getName();      		 //获取文件名+后缀名
		
		filerealname = filename.substring(0, filename.lastIndexOf(".")); //获取不带后缀的文件名-后面加.toLowerCase()小写
		
		System.out.println("----接收到文件("+sourceVideoPath+")需要转换-------");
		
		//检测本地是否存在
		/*if (checkfile(sourceVideoPath)) {
			System.out.println(sourceVideoPath + "========该文件存在哟 ");
			return false;
		}*/
		
		System.out.println("----开始转文件(" + sourceVideoPath + ")-------------------------- ");
		
		//执行转码机制
		if (process(targetExtension,isDelSourseFile)) {
			
			System.out.println("视频转码结束,开始截图================= ");
			
			//视频转码完成,调用截图功能--zoutao
			if (processImg(sourceVideoPath)) {
				System.out.println("截图成功! ");
			} else {
				System.out.println("截图失败! ");
			}
			
			
			//删除原视频+临时视频
			/*if (isDelSourseFile) {
				deleteFile(sourceVideoPath);
			}*/
			
			/*File file1 = new File(sourceVideoPath); 
	         if (file1.exists()){
	        	 System.out.println("删除原文件-可用:"+sourceVideoPath);
	             file1.delete();
	          }*/
			
			String temppath=videofolder + filerealname + ".avi";
			File file2 = new File(temppath); 
	         if (file2.exists()){
	        	 System.out.println("删除临时文件:"+temppath);
	             file2.delete();
	          }	

			sourceVideoPath = null;
			return true;
		} else {
			sourceVideoPath = null;
			return false;
		}
	}
	
	
	/**
	 * 检查文件是否存在-多处都有判断
	 * @param path
	 * @return
	 */
	
	/*private boolean checkfile(String path) {
		path = sourceVideoPath;
		File file = new File(path);
		try {
			if (file.exists()) {
				System.out.println("视频文件不存在============="+path);
				return true;
			} else {
				System.out.println("视频文件存在"+path);
				return false;
			}
		} catch (Exception e) {
			// TODO: handle exception
			System.out.println("拒绝对文件进行读访问");
		}
		return false;
	}*/
	

	/**
	 * 视频截图功能
	 * @param sourceVideoPath 需要被截图的视频路径(包含文件名和后缀名)
	 * @return
	 */
	public boolean processImg(String sourceVideoPath) {
		
		//先确保保存截图的文件夹存在
		File TempFile = new File(imageRealPath);
		if (TempFile.exists()) {
			if (TempFile.isDirectory()) {
				System.out.println("该文件夹存在。");
			}else {
				 System.out.println("同名的文件存在,不能创建文件夹。");
			}
		}else {
			 System.out.println("文件夹不存在,创建该文件夹。");
			 TempFile.mkdir();
		}
		
		File fi = new File(sourceVideoPath);
		filename = fi.getName();			//获取视频文件的名称。
		filerealname = filename.substring(0, filename.lastIndexOf("."));	//获取视频名+不加后缀名 后面加.toLowerCase()转为小写
		
		List<String> commend = new ArrayList<String>();
		//第一帧: 00:00:01 
		//截图命令:time ffmpeg -ss 00:00:01 -i test1.flv -f image2 -y test1.jpg
		
		commend.add(ffmpegpath);			//指定ffmpeg工具的路径
		commend.add("-ss");
		commend.add("00:00:01");			//1是代表第1秒的时候截图
		commend.add("-i");
		commend.add(sourceVideoPath);		//截图的视频路径
		commend.add("-f");
		commend.add("image2");
		commend.add("-y");
		commend.add(imageRealPath + filerealname + ".jpg");		//生成截图xxx.jpg
		
		 //打印截图命令--zoutao
        StringBuffer test = new StringBuffer();    
        for (int i = 0; i < commend.size(); i++) {    
            test.append(commend.get(i) + " ");    
        }    
        System.out.println("截图命令:"+test);    
		
        //转码后完成截图功能-还是得用线程来解决--zoutao
		try {			
			/*ProcessBuilder builder = new ProcessBuilder();
			builder.command(commend);
			Process p =builder.start();*/
				 //调用线程处理命令
				ProcessBuilder builder = new ProcessBuilder();
				builder.command(commend);
				Process p = builder.start(); 
				
				//获取进程的标准输入流  
				final InputStream is1 = p.getInputStream();   
				//获取进程的错误流  
	            final InputStream is2 = p.getErrorStream(); 
	          //启动两个线程,一个线程负责读标准输出流,另一个负责读标准错误流  
	            new Thread() {    
	                public void run() {    
	                    BufferedReader br = new BufferedReader(    
	                            new InputStreamReader(is1));    
	                    try {    
	                        String lineB = null;    
	                        while ((lineB = br.readLine()) != null) {    
	                            if (lineB != null){    
	                                //System.out.println(lineB);    //必须取走线程信息避免堵塞
	                            }
	                        }    
	                    } catch (IOException e) {    
	                        e.printStackTrace();    
	                    } 
	                    //关闭流
	                    finally{  
	                        try {  
	                          is1.close();  
	                        } catch (IOException e) {  
	                           e.printStackTrace();  
	                       }  
	                     }  
	                    
	                }    
	            }.start();    
	            new Thread() {    
	                public void run() {    
	                    BufferedReader br2 = new BufferedReader(    
	                            new InputStreamReader(is2));    
	                    try {    
	                        String lineC = null;    
	                        while ((lineC = br2.readLine()) != null) {    
	                            if (lineC != null)   { 
	                                //System.out.println(lineC);   //必须取走线程信息避免堵塞
	                            }
	                        }    
	                    } catch (IOException e) {
	                        e.printStackTrace();  
	                    }  
	                    
	                    //关闭流
	                    finally{  
	                        try {  
	                            is2.close();  
	                        } catch (IOException e) {  
	                            e.printStackTrace();  
	                        }  
	                      } 
	                    
	                }    
	            }.start();   
	            // 等Mencoder进程转换结束,再调用ffmepg进程非常重要!!!
	            p.waitFor();
            System.out.println("截图进程结束");
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	
	
	/**
	 * 实际转换视频格式的方法
	 * @param targetExtension 目标视频后缀名
	 * @param isDelSourseFile 转换完成后是否删除源文件
	 * @return
	 */
	private boolean process(String targetExtension, boolean isDelSourseFile) {
		
		//先判断视频的类型-返回状态码
		int type = checkContentType();  
		boolean status = false;
		
		 //根据状态码处理
		if (type == 0) {
			System.out.println("ffmpeg可以转换,统一转为mp4文件");
			
			status = processVideoFormat(sourceVideoPath,targetExtension,isDelSourseFile);//可以指定转换为什么格式的视频
			
		} else if (type == 1) {
			//如果type为1,将其他文件先转换为avi,然后在用ffmpeg转换为指定格式
			System.out.println("ffmpeg不可以转换,先调用mencoder转码avi");
			String avifilepath = processAVI(type);
			
			if (avifilepath == null){
				// 转码失败--avi文件没有得到
				System.out.println("mencoder转码失败,未生成AVI文件");
				return false;
			}else {
				System.out.println("生成AVI文件成功,ffmpeg开始转码:");
				status = processVideoFormat(avifilepath,targetExtension,isDelSourseFile);
			}
		}
		return status;   //执行完成返回布尔类型true
	}

	/**
	 * 检查文件类型
	 * @return
	 */
	private int checkContentType() {
		
		//取得视频后缀-
		String type = sourceVideoPath.substring(sourceVideoPath.lastIndexOf(".") + 1, sourceVideoPath.length()).toLowerCase();
		System.out.println("源视频类型为:"+type);
		
		 // 如果是ffmpeg能解析的格式:(asx,asf,mpg,wmv,3gp,mp4,mov,avi,flv等)
		if (type.equals("avi")) {
			return 0;
		} else if (type.equals("mpg")) {
			return 0;
		} else if (type.equals("wmv")) {
			return 0;
		} else if (type.equals("3gp")) {
			return 0;
		} else if (type.equals("mov")) {
			return 0;
		} else if (type.equals("mp4")) {
			return 0;
		} else if (type.equals("asf")) {
			return 0;
		} else if (type.equals("asx")) {
			return 0;
		} else if (type.equals("flv")) {
			return 0;
		}else if (type.equals("mkv")) {
			return 0;
		}
		
		
		// 如果是ffmpeg无法解析的文件格式(wmv9,rm,rmvb等),  
        // 就先用别的工具(mencoder)转换为avi(ffmpeg能解析的)格式.  
		else if (type.equals("wmv9")) {
			return 1;
		} else if (type.equals("rm")) {
			return 1;
		} else if (type.equals("rmvb")) {
			return 1;
		}
		System.out.println("上传视频格式异常");
		return 9;
	}


	
	/**
	 *  对ffmpeg无法解析的文件格式(wmv9,rm,rmvb等), 
	 *  可以先用(mencoder)转换为avi(ffmpeg能解析的)格式.再用ffmpeg解析为指定格式
	 * @param type
	 * @return
	 */
	private String processAVI(int type) {
		
		System.out.println("调用了mencoder.exe工具");
		List<String> commend = new ArrayList<String>();
		
		commend.add(mencoderpath);                //指定mencoder.exe工具的位置
		commend.add(sourceVideoPath);             //指定源视频的位置
		commend.add("-oac");
		commend.add("mp3lame");			//lavc 原mp3lame
		commend.add("-lameopts");
		commend.add("preset=64");
		commend.add("-ovc");
		commend.add("xvid"); 		//mpg4(xvid),AVC(h.264/x264),只有h264才是公认的MP4标准编码,如果ck播放不了,就来调整这里
		commend.add("-xvidencopts");  //xvidencopts或x264encopts 
		commend.add("bitrate=600");		//600或440
		commend.add("-of");
		commend.add("avi");
		commend.add("-o");
		
		commend.add(videofolder + filerealname + ".avi");   //存放路径+名称,生成.avi视频
		
		//打印出转换命令-zoutao
        StringBuffer test = new StringBuffer();    
        for (int i = 0; i < commend.size(); i++) {    
            test.append(commend.get(i) + " ");    
        }
        System.out.println("mencoder输入的命令:"+test);
     // cmd命令:mencoder 1.rmvb -oac mp3lame -lameopts preset=64 -ovc xvid
     // -xvidencopts bitrate=600 -of avi -o rmvb.avi
        
		try {
			 //调用线程命令启动转码
			ProcessBuilder builder = new ProcessBuilder();
			builder.command(commend);
			Process p = builder.start();   //多线程处理加快速度-解决数据丢失
			//doWaitFor(p);
			
			//获取进程的标准输入流  
			final InputStream is1 = p.getInputStream();   
			//获取进程的错误流  
            final InputStream is2 = p.getErrorStream(); 
          //启动两个线程,一个线程负责读标准输出流,另一个负责读标准错误流  
            new Thread() {    
                public void run() {    
                    BufferedReader br = new BufferedReader(    
                            new InputStreamReader(is1));    
                    try {    
                        String lineB = null;    
                        while ((lineB = br.readLine()) != null) {    
                            if (lineB != null){    
                                System.out.println(lineB);    //打印mencoder转换过程代码-可注释
                            }
                        }    
                    } catch (IOException e) {    
                        e.printStackTrace();    
                    } 
                    
                    //关闭流
                   /* finally{  
                        try {  
                          is1.close();  
                        } catch (IOException e) {  
                           e.printStackTrace();  
                       }  
                     }  */
                    
                }    
            }.start();    
            new Thread() {    
                public void run() {    
                    BufferedReader br2 = new BufferedReader(    
                            new InputStreamReader(is2));    
                    try {    
                        String lineC = null;    
                        while ((lineC = br2.readLine()) != null) {    
                            if (lineC != null)   { 
                                System.out.println(lineC);    //打印mencoder转换过程代码
                            }
                        }    
                    } catch (IOException e) {
                        e.printStackTrace();  
                    }  
                    
                    //关闭
                   /* finally{  
                        try {  
                            is2.close();  
                        } catch (IOException e) {  
                            e.printStackTrace();  
                        }  
                      } */ 
                    
                }    
            }.start();   
    
            // 等Mencoder进程转换结束,再调用ffmepg进程非常重要!!!
            p.waitFor();
            System.out.println("Mencoder进程结束");  
			return videofolder + filerealname + ".avi";		//返回转为AVI以后的视频地址
			
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	
	

	/**
	 * 转换为指定格式--zoutao
	 * ffmpeg能解析的格式:(asx,asf,mpg,wmv,3gp,mp4,mov,avi,flv等)
	 * @param oldfilepath
	 * @param targetExtension 目标格式后缀名 .xxx
	 * @param isDelSourseFile 转换完成后是否删除源文件
	 * @return
	 */
	private boolean processVideoFormat(String oldfilepath, String targetExtension, boolean isDelSourceFile) {
		 
		System.out.println("调用了ffmpeg.exe工具");
		
		//先确保保存转码后的视频的文件夹存在
		File TempFile = new File(targetfolder);
		if (TempFile.exists()) {
			if (TempFile.isDirectory()) {
					System.out.println("该文件夹存在。");
				}else {
						System.out.println("同名的文件存在,不能创建文件夹。");
					}
			}else {
				 System.out.println("文件夹不存在,创建该文件夹。");
				 TempFile.mkdir();
			}
		
		List<String> commend = new ArrayList<String>();
		
		commend.add(ffmpegpath);		 //ffmpeg.exe工具地址
		commend.add("-i");
		commend.add(oldfilepath);			//源视频路径
		
		commend.add("-vcodec");  
        commend.add("h263");  //
        commend.add("-ab");		//新增4条
        commend.add("128");      //高品质:128 低品质:64
        commend.add("-acodec");		
        commend.add("mp3");      //音频编码器:原libmp3lame
        commend.add("-ac");     
        commend.add("2");       //原1 
		commend.add("-ar");
		commend.add("22050");   //音频采样率22.05kHz
		commend.add("-r"); 
	    commend.add("29.97");  //高品质:29.97 低品质:15
		commend.add("-c:v");
		commend.add("libx264");	//视频编码器:视频是h.264编码格式
		commend.add("-strict");
		commend.add("-2");
		commend.add(targetfolder + filerealname + targetExtension);  // //转码后的路径+名称,是指定后缀的视频
		
		//打印命令--zoutao
        StringBuffer test = new StringBuffer();    
        for (int i = 0; i < commend.size(); i++) {    
            test.append(commend.get(i) + " ");    
        }
        System.out.println("ffmpeg输入的命令:"+test);
		
		try {
				//多线程处理加快速度-解决rmvb数据丢失builder名称要相同
				ProcessBuilder builder = new ProcessBuilder();
				builder.command(commend);
				Process p = builder.start();   //多线程处理加快速度-解决数据丢失
				
				final InputStream is1 = p.getInputStream();
	            final InputStream is2 = p.getErrorStream();
	            new Thread() {    
	                public void run() {    
	                    BufferedReader br = new BufferedReader(    
	                            new InputStreamReader(is1));    
	                    try {    
	                        String lineB = null;    
	                        while ((lineB = br.readLine()) != null) {    
	                            if (lineB != null)    
	                                System.out.println(lineB);    //打印mencoder转换过程代码
	                        }    
	                    } catch (IOException e) {    
	                        e.printStackTrace();    
	                    }    
	                }    
	            }.start();    
	            new Thread() {    
	                public void run() {    
	                    BufferedReader br2 = new BufferedReader(    
	                            new InputStreamReader(is2));    
	                    try {    
	                        String lineC = null;    
	                        while ((lineC = br2.readLine()) != null) {    
	                            if (lineC != null)    
	                                System.out.println(lineC);    //打印mencoder转换过程代码
	                        }    
	                    } catch (IOException e) {
	                        e.printStackTrace();  
	                    }    
	                }
	            }.start();	 
	            
	            p.waitFor();		//进程等待机制,必须要有,否则不生成mp4!!!
			System.out.println("生成mp4视频为:"+videofolder + filerealname + ".mp4");
			return true;
			} 
			catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}
}

这个文件就有些复杂了,其中涉及到视频的转码、截图、转码是需要判断视频类型的,

如果是ffmpeg能解析的格式:(asx,asf,mpg,wmv,3gp,mp4,mov,avi,flv等)就用ffmpeg转码成mp4。

如果是ffmpeg无法解析的文件格式(wmv9,rm,rmvb等), 就先用别的工具(mencoder)转换为avi,然后在采用ffmpeg来转码成mp4。

每个视频还会自动生成一张相关的图片。用于前台展示。
其中视频是转码成h.264的编码格式,很多播放器对这个有要求。

这样以后,等web上传一个视频,就会上传,然后转码成mp4,然后数据记录存入sql中。

上传的视频:
这里写图片描述

转码以后的视频:
这里写图片描述

转码后生成的视频截图:
这里写图片描述

在前台做个a标签,附带参数id,当点击视频的对应id以后,就会跳转到videoView视频播放页控制层,查询数据,然后拿到url,然后跳转到播放页面去。

videoView控制层
源码如下:

					/**
					 * @Description:(跳转到后台视频的播放页)
					 * @param:@param request
					 * @param:@param response
					 * @param:@param session
					 * @param:@return
					 * @return:ModelAndView
					 * @author:Zoutao
					 * @date:2018-6-12
					 * @version:V1.0
					 */
					@RequestMapping("/videoView")
					public ModelAndView videoView(HttpServletRequest request,
							HttpServletResponse response, HttpSession session) {
						System.out.println("进入videoView视频播放页控制层");
						ModelAndView model = new ModelAndView("back/playvideo");

						// 获取上传的id
						int id = Integer.parseInt(request.getParameter("id"));
						
						tb_resource resource = null;
						resource = tb_resourceService.getResourceById(id); // 通过id获取资源信息
						System.out.println("查询到的数据:" + resource);

						String url = resource.getUrl();
						System.out.println("地址为:" + url);
						System.out.println("获取文件名filename");

						String suffix = resource.getSuffix();
						System.out.println("后缀名为:" + suffix);

						System.out.println("播放的视频路径:" + url);
						// 调用转换类DocConverter,并将需要转换的文件传递给该类的构造方法
						
						session.setAttribute("VideoName", suffix);	//只需要把文件名字/后缀名注入到session带回前台
						
						System.out.println("全局路径为:" + session.getAttribute("VideoName"));
						
						if (resource!=null) {
							model.addObject("resource",resource); //视频相关信息带到前台
						}
						
						return model;
					}

前台的视频播放页面:
播放器采用video.js插件,因为视频都已经转码成mp4的了,就不在乎原来是什么格式的,任何一个播放器基本上都可以播放mp4.

源码如下:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<%
	String path = request.getContextPath();
	String basePath = request.getScheme() + "://"
			+ request.getServerName() + ":" + request.getServerPort()
			+ path + "/";
%>
<%
	String VideoName = session.getAttribute("VideoName").toString();
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<base href="<%=basePath%>">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link href="css/backstyle.css" rel="stylesheet" type="text/css" />
<link href="css/select.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="<%=basePath%>js/jquery.js"></script>
<script type="text/javascript" src="js/jquery.idTabs.min.js"></script>
<script type="text/javascript" src="js/select-ui.min.js"></script>


<!-- 去掉...引用就是使用video.js播放,不引用就是使用html5的video标签播放 -->
<script src="<%=basePath%>static.../js/video.min.js"></script>
<script src="<%=basePath%>static.../js/zh-CN.js"></script>
<link href="<%=basePath%>static.../css/video-js.min.css" rel="stylesheet">


<script>
  videojs.options.flash.swf = "http://example.com/path/to/video-js.swf"
</script>

<style>
body {
	background-color: #e5ebee
	width: 1000px;
	height: 800px;
}

.m { 
	width: 600px;
	height: 400px;
	margin-left: 20%;
	margin-right: 5%;
	margin-top: 2%;
	margin-bootom: 5%;
}


/* 暂停时显示播放按钮 */
.vjs-paused .vjs-big-play-button,
.vjs-paused.vjs-has-started .vjs-big-play-button {
    display: block;
}

/* 点击屏幕播放/暂停 */
.video-js.vjs-playing .vjs-tech {
    pointer-events: auto;
}
</style>

<!-- 设置宽高自适应 -->
<script type="text/javascript">
var player = videojs('#my-video',{fluid: true},function(){
    console.log('Good to go!');
    this.play(); // if you don't trust autoplay for some reason
})


videojs("my-video", {}, function(){
        //video.js初始化完成后执行
    });

</script>

<title>视频播放</title>
</head>
<body>
	<div class="place">
		<span>位置:</span>
		<ul class="placeul">
			 <!-- <li><a href="back/resourceList?uid=3">视频资源管理</a></li> -->
			<li><a href="javascript:void(0)">视频播放</a></li>
		</ul>
	</div>

	<div class="Name" style="font-size: 15px;margin: 1% 0 0 0;text-align: center;">
		<%-- 视频名称:<%=VideoName%> --%>
		<c:if test="${not empty resource}">
			<td>视频标题:${resource.title}</td>
			<td>上传者:${resource.writer}</td>
			<td>上传时间:${resource.time}</td>
			<td>播放量:${resource.clicks}</td>
		</c:if> 
		
	</div>
		
<!-- class后面加vjs-fluid就设置宽高自适应 ,浏览器自带的控制条,自动预加载,视频封面-->
 <div class="m">
		
		<video 
		id="my-video" 
		class="video-js vjs-big-play-centered" 
		width="800" 
		height="500" 
		align="middle" 
		controls="true" 
		preload="auto" 
		poster="/websiteimages/finshimg/GZNC.jpg" 
		data-setup="{}" >
		
		<source src="/websiteimages/finshvideo/<%=VideoName%>" type="video/mp4" ></source>
		
		<p class="vjs-no-js">
			暂不支持本浏览器 <a href="http://videojs.com/html5-video-support/"
				target="_blank">请将浏览器设置为极速模式,使用html5播放</a></p>
		</video>
		<p style="color:red;margin:20px auto auto 33%;">温馨提示:点击播放器中下载按钮或者鼠标右键另存视频即可下载本视频。</p>
</div> 

<!--这是带下载按钮版本-->
	<!-- <video id="my-video" 
		class="video-js " 
		src="/video/5.mp4" 
		controls="controls"			//浏览器自带的控制条
		preload="auto"
		width="500"			//不能写px
		height="300" 
		poster="/video/1.jpg" >		//视频封面
		您的浏览器不支持播放该视频!
	</video> -->
</body>
</html>

其中/websiteimages,是tomcat虚拟路径,配置的是

<Context docBase="D:\testfile\video\" path="/websiteimages" reloadable="true"/>

src="/websiteimages/finshvideo/<%=VideoName%>" 就是表示播放的是该路径下的某个指定名称的视频文件。

下载视频
源码代码:

				/**
				 * @author:zoutao
				 * @comments(方法的说明):后台各类资源下载+视频下载
				 * @time 2018年05月24日
				 * @returnType:String
				 */
				   @RequestMapping("/downloadfile")
				   public String Download(HttpServletRequest request,HttpServletResponse response) {
							response.setCharacterEncoding("utf-8");
							response.setContentType("text/html;charset=utf-8");
							response.setContentType("multipart/form-data");
							
							String url = request.getParameter("url");
							String fileName= url.substring(url.lastIndexOf('/')+1);
					//		System.out.println(fileName);
							response.setHeader("Content-Disposition", "attachment;fileName="+ fileName);
							
							
							// 获取文件后缀名-通过后缀来判断是不是视频类的
							String filename_extension = fileName.substring(fileName
									.lastIndexOf(".") + 1);
							System.out.println("视频的后缀名:"+filename_extension);
							
							String path="";
							try {
								
								if (filename_extension.equals("mp4") || filename_extension=="mp4" || filename_extension.equals("MP4") || filename_extension=="MP4") {
									 path ="E://Projectpicture/websiteimages/finshvideo/"; //视频文件的位置
								}else {
									 path = "E://Projectpicture/websiteimages/"; 	//其他文件的位置
								}
								
								InputStream inputStream = new FileInputStream(new File(path+ fileName));
								System.out.println(path+ fileName);

								OutputStream os = response.getOutputStream();
								byte[] b = new byte[2048];
								int length;
								while ((length = inputStream.read(b)) > 0) {
									os.write(b, 0, length);
								}
								os.close();
								inputStream.close();
							} catch (FileNotFoundException e) {
								e.printStackTrace();
							} catch (IOException e) {
								e.printStackTrace();
							}
					            //  返回值要注意,要不然就出现下面这句错误!
					            //java+getOutputStream() has already been called for this response
							return null;
						}
				   

也是采用流的方式来下载即可。只需要找对文件存储的位置,这样就可以直接下载。

最终效果:

前台,点击相关的图片,跳转到视频播放页面
这里写图片描述

播放视频:
这里写图片描述

点击下载:
这里写图片描述

后台查询数据:
这里写图片描述

播放视频和下载视频跟前台一样。

上传视频:(QQ不是我的)
这里写图片描述

这个就是一整个关于web/java实现多种格式视频上传、转码、播放、下载等功能,主要的方法已经在文中给出,只需要根据参数,提供参数即可调用。

需要源码的话,可以留下邮箱。


You got a dream, you gotta protect it.
如果你有梦想的话,就要去捍卫它 。 ——《当幸福来敲门》

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页