Java使用ffmpeg进行视频切片

视频转MP4执行命令

ffmpeg -i C:\Users\Administrator\Desktop\111\疾速追杀.mkv -y -metadata:s:v rotate=0 -y -c:v libx264 -c:a mp3 -strict -1 C:\Users\Administrator\Desktop\111\疾速追杀.mp4 -metadata:s:v rotate=0 -cpu-used 1

 说明:

-metadata:s:v rotate=0 是用来取消视频元数据翻转的,部分手机拍出来的视频会有翻转现象产生,这个参数是用来重置视频为正常方向的

-strict -1 这个参数表示使用严格级别进行编码,编码器会完全遵循编码规范。使用这个参数是因为某次在解析mov格式视频时命令执行失败,加上此参数问题解决。

MP4转M3U8切片执行命令

ffmpeg -i C:\Users\Administrator\Desktop\111\疾速追杀.mp4 -codec:v copy -codec:a copy -map 0 -f ssegment -segment_format mpegts -segment_list C:\Users\Administrator\Desktop\aaa\疾速追杀.m3u8 -segment_time 10 C:\Users\Administrator\Desktop\aaa\疾速追杀%05d.ts -cpu-used 1

注意:其他格式视频也可以直接转M3U8,但是MKV格式视频貌似有一部分会失败,失败原因可能是因为有些MKV音频多音轨相关,需要先转成MP4。

工具类代码

/**
 * @author: wxw
 * @time: 2023/2/7 13:13
 * @description:
 */

package cn.piesat.space.obs.utils;

import cn.piesat.space.obs.constants.ObsConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.io.IOUtils;
import org.springframework.http.MediaType;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import ws.schild.jave.process.ProcessWrapper;
import ws.schild.jave.process.ffmpeg.DefaultFFMPEGLocator;

import java.io.*;
import java.util.Calendar;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author ruibo.duan, <ruibo.duan@mobvoi.com>
 * @since 2021/7/22
 */
@Slf4j
public class FfmpegUtil {
    private FfmpegUtil() {
    }

    private static void blockFfmpeg(BufferedReader br) throws IOException {
        String line;
        // 该方法阻塞线程,直至合成成功
        while ((line = br.readLine()) != null) {
            doNothing(line);
        }
    }

    private static void doNothing(String line) {
        // FIXME: 2023/1/31 正式使用时注释掉此行,仅用于观察日志
        System.out.println(line);
    }

//    public static void delFile(){
//        String targetPath = ObsConstant.TARGET_MP4_PATH;
//        File srcDir = new File(targetPath);
//        if (!srcDir.exists()) {
//            return;
//        }
//        File[] files = srcDir.listFiles();
//        if (files == null || files.length <= 0) {
//            return;
//        }
//        Date today = new Date();
//        for (int i = 0; i < files.length; i++) {
//            try {
//                File ff = files[i];
//                long time=ff.lastModified();
//                Calendar cal=Calendar.getInstance();
//                cal.setTimeInMillis(time);
//                Date lastModified = cal.getTime();
//                long days = getDistDates(today, lastModified);
//                if(days>0){
//                    if (ff.isDirectory()){
//                        File[] ffFiles = ff.listFiles();
//                        if (ffFiles == null || ffFiles.length <= 0) {
//                            continue;
//                        }
//                        for (int j = 0; j < ffFiles.length; j++) {
//                            if (ffFi les[j].isFile()){
//                                ffFiles[j].delete();
//                            }
//                        }
//                    }
//                    ff.delete();
//                }
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
//        }
//    }

    public static long getDistDates(Date startDate, Date endDate) {
        long totalDate = 0;
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(startDate);
        long timestart = calendar.getTimeInMillis();
        calendar.setTime(endDate);
        long timeend = calendar.getTimeInMillis();
        totalDate = Math.abs((timeend - timestart)) / (1000 * 60 * 60 * 24);
        return totalDate;
    }

    /**
     * 转MP4
     *
     * @return
     */
    public static String processAnyVideo2Mp4(String inputFilePath, String targetPath) {
        try {
            long currentMillis = System.currentTimeMillis();
            ProcessWrapper ffmpeg = new DefaultFFMPEGLocator().createExecutor();
            ffmpeg.addArgument("-i");
            ffmpeg.addArgument(inputFilePath);
            ffmpeg.addArgument("-metadata:s:v");
            ffmpeg.addArgument("rotate=0");
            ffmpeg.addArgument("-y");
            ffmpeg.addArgument("-c:v");
            ffmpeg.addArgument("libx264");
            ffmpeg.addArgument("-c:a");
            ffmpeg.addArgument("mp3");
            ffmpeg.addArgument(targetPath);
            ffmpeg.addArgument("-cpu-used");
            ffmpeg.addArgument("1");
            ffmpeg.execute();
            try (BufferedReader br = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()))) {
                blockFfmpeg(br);
            }
            return targetPath;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取视频时长
     */
    public static long getVideoDuration(String inputFilePath) throws Exception {
        long videoDuration = 0;
        ProcessWrapper process = new DefaultFFMPEGLocator().createExecutor();
        process.addArgument("-i");
        process.addArgument(inputFilePath);
        process.execute();
        try (BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
            String line;
            while ((line = br.readLine()) != null) {
                if (line.contains("Duration:")) {
                    videoDuration = cutVideoDuration(line);
                    break;
                }
            }
        }
        return videoDuration;
    }

    /**
     * 截取ffmpeg视频信息中的视频时长
     */
    public static long cutVideoDuration(String line) throws Exception {
        Pattern pattern = Pattern.compile(ObsConstant.FFMPEG_DURATION_REGEX);
        Matcher matcher = pattern.matcher(line);
        long duration = 0L;
        if (matcher.find()) {
            // 计算时长
            long hour = Long.parseLong(matcher.group(1));//group1为第一个括号内的内容
            long minute = Long.parseLong(matcher.group(2));//group2为第二个括号内容
            long second = Long.parseLong(matcher.group(3));//group3为第二个括号内容
            long millisecond = Long.parseLong(matcher.group(4));//group4为第二个括号内容
            duration = hour * 3600000 + minute * 60000 + second * 1000 + millisecond;
        } else {
            System.out.println("NOT FOUND");
        }
        return duration;
    }

    public static void videoToM3u8(String inputFilePath, String m3u8OutputPath, String tsOutputPath) throws Exception {
        ProcessWrapper process = new DefaultFFMPEGLocator().createExecutor();
        process.addArgument("-i");
        process.addArgument(inputFilePath);
        process.addArgument("-codec:v");
        process.addArgument("copy");
        process.addArgument("-codec:a");
        process.addArgument("copy");
        process.addArgument("-map");
        process.addArgument("0");
        process.addArgument("-f");
        process.addArgument("ssegment");
        process.addArgument("-segment_format");
        process.addArgument("mpegts");
        process.addArgument("-segment_list");
        process.addArgument(m3u8OutputPath);
        process.addArgument("-segment_time");
        process.addArgument("10");
        process.addArgument("-cpu-used");
        process.addArgument("1");
        process.addArgument(tsOutputPath);
        process.execute();
        try (BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
            blockFfmpeg(br);
        }
    }

    public static void videoScreenshot(String inputFilePath, String screenshotOutputPath) throws Exception {
        try {
            ProcessWrapper command = new DefaultFFMPEGLocator().createExecutor();
            // 创建一个List集合来保存从视频中截取图片的命令
            command.addArgument("-i");
            command.addArgument(inputFilePath); // 同上(指定的文件即可以是转换为flv格式之前的文件,也可以是转换的flv文件)
            command.addArgument("-y");
            command.addArgument("-f");
            command.addArgument("image2");
            command.addArgument("-ss"); // 添加参数"-ss",该参数指定截取的起始时间
            command.addArgument("1"); // 添加起始时间为第1秒
            command.addArgument("-t"); // 添加参数"-t",该参数指定持续时间
            command.addArgument("0.001"); // 添加持续时间为1毫秒
            command.addArgument("-cpu-used");
            command.addArgument("1");
//            command.addArgument("-s"); // 添加参数"-s",该参数指定截取的图片大小
//        command.addArgument("0*0"); // 添加截取的图片大小为350*240
            command.addArgument(screenshotOutputPath); // 添加截取的图片的保存路径
            command.execute();
            try (BufferedReader br = new BufferedReader(new InputStreamReader(command.getErrorStream()))) {
                blockFfmpeg(br);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static MultipartFile File2MultipartFile(File file) {
        FileItem item = new DiskFileItemFactory().createItem("file", MediaType.MULTIPART_FORM_DATA_VALUE, true, file.getName());
        try (InputStream input = new FileInputStream(file); OutputStream os = item.getOutputStream()) {
            IOUtils.copy(input, os);
        } catch (Exception e) {
            throw new IllegalArgumentException("Invalid file: " + e, e);
        }
        return new CommonsMultipartFile(item);
    }

    /**
     * 常用命令
     *
     * @return
     */
    public static void cmd() {
        // FIXME: 2023/1/31  还有很多类似命令 不再一一列举 ,附上命令,具体写法参考 getTargetThumbnail或addSubtitle方法
        // FIXME: 2023/1/31 ffmpeg命令网上搜索即可

        // 剪切视频
        // ffmpeg -ss 00:00:00 -t 00:00:30 -i test.mp4 -vcodec copy -acodec copy output.mp4
        // * -ss 指定从什么时间开始
        // * -t 指定需要截取多长时间
        // * -i 指定输入文件

        // ffmpeg -ss 10 -t  15 -accurate_seek -i test.mp4 -codec copy cut.mp4
        // ffmpeg -ss 10 -t 15 -accurate_seek -i test.mp4 -codec copy -avoid_negative_ts 1 cut.mp4

        // 拼接MP4
        // 第一种方法:
        // ffmpeg -i "concat:1.mp4|2.mp4|3.mp4" -codec copy out_mp4.mp4
        // 1.mp4 第一个视频文件的全路径
        // 2.mp4 第二个视频文件的全路径

        // 提取视频中的音频
        // ffmpeg -i input.mp4 -acodec copy -vn output.mp3
        // -vn: 去掉视频;-acodec: 音频选项, 一般后面加copy表示拷贝

        // 音视频合成
        // ffmpeg -y –i input.mp4 –i input.mp3 –vcodec copy –acodec copy output.mp4
        // -y 覆盖输出文件

        // 剪切视频
        //  ffmpeg -ss 0:1:30 -t 0:0:20 -i input.mp4 -vcodec copy -acodec copy output.mp4
        // -ss 开始时间; -t 持续时间

        // 视频截图
        //  ffmpeg –i test.mp4 –f image2 -t 0.001 -s 320x240 image-%3d.jpg
        // -s 设置分辨率; -f 强迫采用格式fmt;

        // 视频分解为图片
        //   ffmpeg –i test.mp4 –r 1 –f image2 image-%3d.jpg
        // -r 指定截屏频率

        // 将图片合成视频
        //  ffmpeg -f image2 -i image%d.jpg output.mp4

        // 视频拼接
        //  ffmpeg -f concat -i filelist.txt -c copy output.mp4

        // 将视频转为gif
        //    ffmpeg -i input.mp4 -ss 0:0:30 -t 10 -s 320x240 -pix_fmt rgb24 output.gif
        // -pix_fmt 指定编码

        // 视频添加水印
        //  ffmpeg -i input.mp4 -i logo.jpg
        // -filter_complex[0:v][1:v]overlay=main_w-overlay_w-10:main_h-overlay_h-10[out] -map [out] -map
        // 0:a -codec:a copy output.mp4
        // main_w-overlay_w-10 视频的宽度-水印的宽度-水印边距;

    }
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值