ffmpeg解析视频

windows的ffmpeg包下载地址:https://ffmpeg.zeranoe.com/builds/

开发环境使用的windows包,生产环境需要下载linux的包

包的结构是这样的:

进入bin目录

这个.exe文件就是我解析视频使用的工具

文件上传略,一起看一下后台使用ffmpeg的方法:

@ResponseBody
@RequestMapping("/upload")
public Object upload(@RequestParam("file") MultipartFile file)throws Exception{
    File localFile = new File("本地的路径(不带文件名)","文件名(xxx.mp4)");
    file.transferTo(localFile);
    ProcessVideo processVideo = new ProcessVideo("本地的路径(不带文件名)","视频分段后保存的路径","文件名(xxx.mp4)","这里是ffmpeg.exe的全路径");
    processVideo.run();
    return "ok";
}

ProcessVideo 是一个线程类,看代码可以知道,文件上传到本地,程序就结束了,解析视频的过程是一个单独的线程再跑。

解析的过程,日志打印是这样的,像心跳一样一行一行的跳:

解析之后的效果是这样的:

一个.m3u8格式的文件,多个.ts文件,m3u8文件中记录了.ts文件名列表,想一个节目表单,这样顺序播放

解析需要用到的三个工具类

@Slf4j
public class ProcessVideo extends Thread {

    private String source;
    private String target;
    private String fileName;
    private String ffmpeg;
    private VideoConvert videoConvert;

    public ProcessVideo(String source, String target, String fileName, String ffmpeg){
        this.source = source;
        this.target = target;
        this.fileName = fileName;
        this.ffmpeg = ffmpeg;
    }

    @Override
    public void run() {
        VideoConvert convert = new VideoConvert(ffmpeg);
        String sourceFile = source + "/" +fileName;
        log.info("fileName============="+fileName);
        String nameStr = fileName;
        String[] split = nameStr.split("\\.");
        log.info("长度是=============="+split.length);
        log.info("资源路径================"+sourceFile);
        File file = new File(sourceFile);
        long length = file.length();
        log.info("length==="+length);
        long l = length / 1024 / 1024;
        log.info("此文件的大小是===="+l+"M");
        log.info("split======="+split);
        String name = split[0];
        String videoList = target + "/" + name + ".m3u8";
        log.info("目标路径================"+videoList);
        try {
            convert.start(sourceFile, videoList);
        }catch (Exception e){
            e.printStackTrace();
            log.error("",e);
        }
        videoConvert = convert;
        log.info("分割视频方法最后一行");
    }
}
public class VideoConvert {
    private String ffmpeg = "ffmpeg";
    private List<String> cmdList;
    private static Process p;
    private String status;

    private String source;
    private String target;
    private VideoConvert() {}

    /**
     * 初始化:指定ffmpeg路径
     * @param ffmpegPath
     */
    public VideoConvert(String ffmpegPath){
        this.ffmpeg = ffmpegPath;
    }

    /**
     * 开始转换
     * @param source 目前只支持mp4格式,源视频路径
     * @param target 输出路径,必须是m3u8格式。
     * @throws InterruptedException
     * @throws FileNotFoundException
     */
    public void start(String source, String target) throws InterruptedException, FileNotFoundException {
        this.source = source;
        this.target = target;
//        checkFfmpeg();
        checkSource();
        processM3U8(source, target);
    }

    private void checkSource() throws FileNotFoundException {
        File fm = new File(source);
        if (!fm.exists()) {
            throw new FileNotFoundException("source不存在:" + source);
        }
    }

    /**
     * 拼接ffmpeg命令:ffmpeg -i test.mp4 -c:v libx264 -hls_time 60 -hls_list_size 0 -c:a aac -strict -2 -f hls output.m3u8
     * @param source
     * @return
     */
    private boolean processM3U8(String source, String target) {
        File targetFile = new File(target);
        File parentDir  = targetFile.getParentFile();
        if (!parentDir.exists()) {
            parentDir.mkdirs();
        }
        List<String> commend = new ArrayList<String>();
        commend.add(ffmpeg);
        commend.add("-i");
        commend.add(source);
        commend.add("-c:v");
        commend.add("libx264");
        commend.add("-hls_time");
        commend.add("60");
        commend.add("-hls_list_size");
        commend.add("0");
        commend.add("-c:a");
        commend.add("aac");
        commend.add("-strict");
        commend.add("-2");
        commend.add("-f");
        commend.add("hls");
        commend.add(target);
        this.cmdList = commend;
        // 通过ProcessBuilder创建
//        processBuilder(commend);

        // 通过runtime创建
        runtimeBuilder(getCommand());
        return true;
    }

    /**
     * 使用ProcessBuilder类调用cmd
     * @param command cmd命令组合,list集合,元素不能包含空格,否则可能会报错
     * @return
     */
    private static boolean processBuilder(List<String> command){
        try {
            ProcessBuilder builder = new ProcessBuilder(command);
            builder.command(command);
            new Thread(){
                @Override
                public void run(){
                    try {
                        Process p = builder.start();
                        VideoConvert.p = p;
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }.start();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 检查ffmpeg是否有效
     * @throws FileNotFoundException
     */
    private void checkFfmpeg() throws FileNotFoundException {
        File fm = new File(ffmpeg);
        if (!fm.exists()) {
            throw new FileNotFoundException("ffmpeg 不存在:" + ffmpeg);
        }
    }

    /**
     * 获取状态
     * @return
     */
    public String getStatus(){
        if (p == null) {
            setStatus(VideoStatus.NO_STARTED);
        }
        if (p.isAlive()) {
            setStatus(VideoStatus.PROCESSING);
        } else {
            setStatus(VideoStatus.COMPLETED);
        }
        return status;
    }


    /**
     * 使用runtime形式调用cmd
     * @param cmdStr
     */
    private static void runtimeBuilder(String cmdStr){
        try {
            Process process = Runtime.getRuntime().exec(cmdStr);
            VideoConvert.p = process;
            VideoThread myThread = new VideoThread(process);
            myThread.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void setStatus(String status){
        this.status = status;
    }

    /**
     * 获取执行的cmd命令
     * @return
     */
    public String getCommand(){
        StringBuffer bf = new StringBuffer();
        for (String str : cmdList) {
            bf.append(" ").append(str);
        }
        return bf.toString();
    }

    public static class VideoStatus{
        public static final String COMPLETED = "completed";
        public static final String PROCESSING = "processing";
        public static final String NO_STARTED = "no started";
    }
}
@Slf4j
public class VideoThread extends Thread {
    private Process p;
    public VideoThread(Process p){
        this.p = p;
    }

    @Override
    public void run(){
        BufferedReader err = new BufferedReader(new InputStreamReader(p.getErrorStream()));
        String line = null;
        try {
            while ((line = err.readLine()) != null) {
                log.info(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                err.close();
                try {
                    p.waitFor();
                    p.destroy();
                    log.info("video convert completed...");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值