java使用ffmpeg转码并上传视频

java使用ffmpeg转码并上传视频

1、ffmpeg的安装和环境配置(windows操作系统下)

下载地址https://ffmpeg.org/download.html,解压后,选择文件路径至bin文件下,例:C:\Users\xx\Downloads\ffmpeg-20171225-be2da4c-win64-static\bin,将此路径赋值,打开高级系统设置,打开系统环境变量,选择path,新增。

2、实现

2.1 视频转码

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Value;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;



public class ConvertVideoUtil {


    private static final Log log = LogFactory.getLog(ConvertVideoUtil.class);


    private static String inputPath = "";

//    //转换视频的插件
    private static String ffmpegPath = "C:\\Users\\XX\\Downloads\\ffmpeg-20171225-be2da4c-win64-static\\bin\\";


    //水印logo path ,注意盘符后面的"\\\\"
    private static String logoPath="C\\\\:XXXXXXXX/logo.png";

    private static int checkContentType() {
        String type = inputPath.substring(inputPath.lastIndexOf(".") + 1, inputPath.length())
                .toLowerCase();
        // 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;
        }
        // 对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;
        }
        return 9;
    }

    private static boolean checkfile(String path) {
        File file = new File(path);
        if (!file.isFile()) {
            return false;
        }
        return true;
    }

    public static String toMp4(String oldfilepath,String newfilepath) throws Exception {
        if (!checkfile(oldfilepath)) {
            System.out.println(oldfilepath + " is not file");
            return "";
        }
        File convertFile = new File(newfilepath);    //NOSONAR
        List<String> commend = new ArrayList<>();

        commend.add(ffmpegPath + "ffmpeg");
        commend.add("-i");
        commend.add(oldfilepath);
        commend.add("-c:v");
        commend.add("libx264");
        commend.add("-mbd");
        commend.add("0");
        commend.add("-c:a");
        commend.add("aac");
        commend.add("-strict");
        commend.add("-2");
        commend.add("-pix_fmt");
        commend.add("yuv420p");
        commend.add("-movflags");
        commend.add("faststart");
        commend.add(convertFile.getPath());
        Process process = null;
        try {
            ProcessBuilder builder = new ProcessBuilder();
            builder.command(commend);
            process = builder.start();
            byte[] b = new byte[1024];
            int readbytes;
            try (InputStream error = process.getErrorStream(); InputStream is = process.getInputStream()) {
                while ((readbytes = error.read(b)) != -1) {
                    log.error("FFMPEG视频转换进程信息:" + new String(b, 0, readbytes));
                }
                while ((readbytes = is.read(b)) != -1) {
                    log.info("FFMPEG视频转换进程输出内容为:" + new String(b, 0, readbytes));
                }
            } catch (IOException e) {
                log.error("读取FFMPEG转换信息异常", e);
            }
            log.info("视频格式转换为:" + convertFile.getPath());

            HashMap<String, String> dto = new HashMap<String, String>();
            dto.put("ffmpeg_path", ffmpegPath+"ffmpeg.exe");// 必填
            dto.put("input_path", oldfilepath);// 必填
            dto.put("video_converted_path", convertFile.getPath());// 必填
            dto.put("logo", logoPath);// 可选(注意windows下面的logo地址前面要写4个反斜杠,如果用浏览器里面调用servlet并传参只用两个,如
            // d:\\:/ffmpeg/input/logo.png)

            String secondsString = new AddWatermarker().videoTransfer(dto);
            System.out.println("转换共用:" + secondsString + "秒");

        } catch (Exception e) {
            log.error("视频转换 Exception:", e);
            throw new Exception("视频转换失败");
        } finally {
            if (null != process) {
                process.destroy();
            }
        }
        return convertFile.getPath();
    }

}

2.2 视频加图片水印

import java.util.ArrayList;
        import java.util.HashMap;
        import java.util.List;
import java.util.UUID;
import java.util.regex.Matcher;
        import java.util.regex.Pattern;

/**
 * o 给视频加水印
 *
 * @author admin
 *
 */
public class AddWatermarker {
    public static String dealString(String str) {
        Matcher m = java.util.regex.Pattern.compile("^frame=.*").matcher(str);
        String msg = "";
        while (m.find()) {
            msg = m.group();
        }
        return msg;
    }

    /**
     * 如果是数字就是成功的时间(秒数)
     *
     * @param str
     * @return
     */
    public static boolean isNumeric(String str) {
        Pattern pattern = Pattern.compile("[0-9]*");
        Matcher isNum = pattern.matcher(str);
        if (!isNum.matches()) {
            return false;
        }
        return true;
    }

    /**
     * 如果返回不是null的值就是成功(值为转换用时单位:秒)
     *
     * @param instr
     * @return
     */
    public static String returnSecond(String instr) {
        String returnValue = null;
        if (null != instr) {
            String[] a = instr.split("\\.");
            String[] b = a[0].split(":");
            int returnNumber = 0;
            if (null != instr && b[0].length() != 0) {
                returnNumber = Integer.valueOf(b[0]) * 60 * 60 + Integer.valueOf(b[1]) * 60 + Integer.valueOf(b[2]);
                returnValue = String.valueOf(returnNumber);
            } else {
                returnValue = null;
            }
        }
        return returnValue;
    }

    /**
     * 获取视频格式(转码前的格式和转码后的格式都可以调用)
     *
     * @param outputPath
     * @return
     */
    public static String returnVideoFormat(String outputPath) {
        return outputPath.substring(outputPath.lastIndexOf(".") + 1);
    }

    /**
     * @ HashMap<String,String> dto 参数传递对象<br>
     * dto中包含的参数<br>
     * (必填)1.ffmpeg_path:ffmpeg执行文件地址,如 d:\\ffmpeg\\ffmpeg.exe
     * Linux下直接调用ffmpeg命令(当然你事先已经有这个程序了)<br>
     * (必填)2.input_path:原视频路径<br>
     * (必填)3.video_converted_path:转换后视频输出路径<br>
     * (可选)4.screen_size:视频尺寸 长度乘宽度 乘号用英文小写"x"如 512x480<br>
     * (可选)5.logo:水印地址(其实在ffmpeg中有一个专门的watermark参数,logo跟它有何不同,我还没看,不过对我来说效果一样
     * 貌似需要png图片才行)<br>
     * (可选,如果填写必须有logo才行,默认为0)6.xaxis:水印logo的横坐标(只有logo参数为一个正确路径才行) 比如0<br>
     * (可选,如果填写必须有logo才行,默认为0)6.yaxis:水印logo的纵坐标(只有logo参数为一个正确路径才行) 比如0<br>
     * (可选)vb:视频比特率,传入一个数值,单位在程序里面拼接了k (可选)ab:音频比特率,传入一个数值,单位在程序里面拼接了k
     */
    public String videoTransfer(HashMap<String, String> dto) {
        // String ffmpeg_path,String input_path,String video_converted_path,String
        // logo,String screen_size,String xaxis,String yaxis,String vb,String ab
        List<String> cmd = new ArrayList<String>();
        cmd.add(dto.get("ffmpeg_path"));
        cmd.add("-y");
        cmd.add("-i");
        cmd.add(dto.get("input_path"));
        if (null != dto.get("screen_size")) {
            cmd.add("-s");
            cmd.add(dto.get("screen_size"));
        }
        if (null != dto.get("logo")) {
            String logo = dto.get("logo");
            cmd.add("-vf");
            String xaxis = dto.get("xaxis");
            String yaxis = dto.get("yaxis");
            xaxis = xaxis != null && !xaxis.equals("") ? xaxis : "0";
            yaxis = yaxis != null && !yaxis.equals("") ? yaxis : "0";

//            String logoString = "movie=" + logo + "[logo],[in][logo]overlay=x=" + xaxis + ":y=" + yaxis + "[out]";  //左上角
//            String logoString = "movie=" + logo + "[logo],[in][logo]overlay=" +"10:main_h-overlay_h-10";    //左下角
            String logoString = "movie=" + logo + "[logo],[in][logo]overlay=" +"main_w-overlay_w-20:main_h-overlay_h-50";    //右下角

            cmd.add(logoString);
        }
        cmd.add("-strict");
        cmd.add("-2");
        if (null != dto.get("vb") && !dto.get("vb").equals("")) {
            cmd.add("-vb");
            cmd.add(dto.get("vb") + "k");
        }
        if (null != dto.get("ab") && !dto.get("ab").equals("")) {
            cmd.add("-ab");
            cmd.add(dto.get("ab") + "k");
        }
        cmd.add("-q:v");
        cmd.add("4");
        cmd.add(dto.get("video_converted_path"));
        String converted_time = CmdExecuter.exec(cmd);
        return returnSecond(converted_time);// 获取转换时间
    }
}


2.3 系统执行

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.List;

public class CmdExecuter {
    public static String exec(List<String> cmd) {
        String converted_time = null;
        Process proc = null;
        BufferedReader stdout = null;
        try {
            ProcessBuilder builder = new ProcessBuilder();
            builder.command(cmd);
            builder.redirectErrorStream(true);
            proc = builder.start();
            stdout = new BufferedReader(new InputStreamReader(proc.getInputStream()));
            String line;
            int lineNumber = 1;
            List<String> returnStringList = new LinkedList<String>();
            while ((line = stdout.readLine()) != null) {
//                System.out.println("第" + lineNumber + "行:" + line);
                lineNumber = lineNumber + 1;
                returnStringList.add(AddWatermarker.dealString(line));
            }
            String info = "";
            for (int i = returnStringList.size() - 1; i >= 0; i--) {
                if (null != returnStringList.get(i) && returnStringList.get(i).startsWith("frame=")) {
                    info = returnStringList.get(i);
                    break;
                }
            }
            if (null != info) {
                converted_time = info.split("time=")[1].split("bitrate=")[0].trim();
            }
        } catch (IndexOutOfBoundsException ex) {
            converted_time = null;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                proc.waitFor();
                stdout.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return converted_time;
    }

}

3、视频流播放

public void getVideo(HttpServletRequest request, HttpServletResponse response, String id) {
        //视频资源存储信息
        response.reset();
        //获取从那个字节开始读取文件
        String rangeString = request.getHeader("Range");
        try {
            //获取响应的输出流
            OutputStream outputStream = response.getOutputStream();

            String uploadPath = hospitalVideoService.getViewPath(id).getUploadPath();
            String fileName = hospitalVideoService.getViewPath(id).getOriginalFileName();
            System.out.println("viewPath = " + uploadPath);

            String filePath = basicPath+uploadPath;
            System.out.println("filePath = " + filePath);

            File file = new File(filePath);
            if(file.exists()){
                RandomAccessFile targetFile = new RandomAccessFile(file, "r");
                long fileLength = targetFile.length();
                //播放
                if(rangeString != null){

                    long range = Long.parseLong(rangeString.substring(rangeString.indexOf("=") + 1, rangeString.indexOf("-")));
                    //设置内容类型
                    response.setHeader("Content-Type", "video/mov");
                    //设置此次相应返回的数据长度
                    response.setHeader("Content-Length", String.valueOf(fileLength - range));
                    //设置此次相应返回的数据范围
                    response.setHeader("Content-Range", "bytes "+range+"-"+(fileLength-1)+"/"+fileLength);
                    //返回码需要为206,而不是200
                    response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
                    //设定文件读取开始位置(以字节为单位)
                    targetFile.seek(range);

                }else {//下载

                    //设置响应头,把文件名字设置好
                    response.setHeader("Content-Disposition", "attachment; filename="+fileName );
                    //设置文件长度
                    response.setHeader("Content-Length", String.valueOf(fileLength));
                    //解决编码问题
                    response.setHeader("Content-Type","application/octet-stream");
                }


                byte[] cache = new byte[1024 * 300];
                int flag;
                while ((flag = targetFile.read(cache))!=-1){
                    outputStream.write(cache, 0, flag);
                }
            }else {
                String message = "file:"+fileName+" not exists";
                //解决编码问题
                response.setHeader("Content-Type","application/json");
                outputStream.write(message.getBytes(StandardCharsets.UTF_8));
            }

            outputStream.flush();
            outputStream.close();

        } catch (FileNotFoundException e) {

        } catch (IOException e) {

        }
    }
4、计算文件大小
public  String ReadVideoSize(File source) {
        FileChannel fc = null;
        String size = "";
        try {
            FileInputStream fis = new FileInputStream(source);
            fc = fis.getChannel();
            BigDecimal fileSize = new BigDecimal(fc.size());
            size = fileSize.divide(new BigDecimal(1024 * 1024), 2, RoundingMode.HALF_UP) + "MB";
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != fc) {
                try {
                    fc.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return size;
    }
5.前端播放(VUE)
<video controls="controls" controls="controls">
  <source src="http://ip:port/xxxxxxx" type="video/mp4" />
</video>

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值