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

版权声明:本文为博主原创文章,未经博主允许不得转载,追究法律责任。 https://blog.csdn.net/ITBigGod/article/details/81747046

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;
                        }

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

最终效果:

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

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

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

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

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

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

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

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


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

猜你喜欢

转载自blog.csdn.net/ITBigGod/article/details/81747046