FFmpeg视频流切片ts

该代码示例展示了如何使用ffmpeg进行视频切片,包括直接生成多个小ts文件和先转换为大ts文件再切分的方法。在Windows系统中,第一种方法较快,而在Linux系统中,第二种方法更快。代码还包含了ffmpeg命令的执行以及异常处理,以及利用多线程池进行并发处理。此外,还包括了视频转码和添加水印的功能。
摘要由CSDN通过智能技术生成

视频支持avi、mp4、flv等格式文件

fmpeg 的两种切片方式,一是直接切片成多个小ts文件,二是先转换成一个大ts文件,再切分成多个小ts文件; windows系统第一种方式快,linux系统第二种方式快;
(问题仍没找到原因)
maven依赖

		 <dependency>
            <groupId>ws.schild</groupId>
            <artifactId>jave-all-deps</artifactId>
            <version>3.1.1</version>
        </dependency>
package com.meta.util;

import com.amazonaws.services.cloudsearchdomain.model.SearchResult;
import com.fajiajia.meta.common.constant.Constants;
import com.fajiajia.meta.exception.ServiceException;
import com.fajiajia.meta.model.base.FileInfo;
import com.fajiajia.meta.service.base.FileInfoService;
import lombok.extern.log4j.Log4j2;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;
import java.util.concurrent.*;

/**
 * @Description:(cmd方式调用ffmpeg使用)
 */
@Log4j2
@Component
public class FfmpegCmdUtil {

    @Resource
    FileInfoService fileInfoService;
    @Resource
    RedisTemplate redisTemplate;

    //执行成功0,失败1
    private static int CODE_SUCCESS = 0;
    private static int CODE_FAIL = 1;
    //将荣耀视频测试.mp4转换荣耀视频测试_转码.mov格式
//  private static String cmd_mp4_2_mov = " -i E:\\IdeaProjects\\judicial-info-society-backend\\file\\2022\\05\\16\\fb3a5850-2132-40c7-aa41-e98c3f4f429d.mp4 -c copy E:\\IdeaProjects\\judicial-info-society-backend\\file\\2022\\05\\16\\999999999999999.mov ";
    //private static String cmd_mp4_2_ts = " -y -i E:\\IdeaProjects\\judicial-info-society-backend\\file\\2022\\05\\16\\fb3a5850-2132-40c7-aa41-e98c3f4f429d.mp4  -vcodec copy -acodec copy -vbsf h264_mp4toannexb D:\\test-ffmpeg\\荣耀视频测试_ts.ts ";
    private static String cmd_mp4_2_ts = " -i  D:\\e2c127e6f200500a1161a9071c4fa795.mp4   -hls_time 30 -hls_list_size 0  -hls_segment_filename D:/file/lucene_%05d.ts D:/file/lucene.m3u8";
    //将荣耀视频测试_转码.mov添加水印(2356899074@qq.com)荣耀视频测试_转码_水印.mov
    private static String cmd_mov_water = " -i I:\\荣耀视频测试_转码.mov -vf \"drawtext=fontfile=Arial.ttf:text='2356899074@qq.com':y=h-line_h-20:x=(w-text_w)/2:fontsize=34:fontcolor=yellow:shadowy=2\" -b:v 3000k I:\\\\荣耀视频测试_转码_水印.mov ";

    //多线程
    private static int core = Runtime.getRuntime().availableProcessors();
    private static ExecutorService pool = new ThreadPoolExecutor(core,//核心
            core * 2,//最大
            0L,//空闲立即退出
            TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>(1024),//无边界阻塞队列
            new ThreadPoolExecutor.AbortPolicy());

    /**
     * 第一步:mp4转mov 第二步:mov添加水印
     *
     * @throws
     * @param: @param args
     * @return: void
     */
    public static void main(String[] args) {

    String aa = "-y -i C:\\Users\\Fadada\\Videos\\e79c541f2a473bb1ff87d94c31499c44.mp4  -vcodec copy -acodec copy -vbsf h264_mp4toannexb  D:\\test\\fb3a5850-2132-40c7-aa41-e98c3f4f429d12.ts";
        String bb = "-i C:\\Users\\Fadada\\Videos\\e79c541f2a473bb1ff87d94c31499c44.mp4 -hls_time 60 -hls_list_size 0 -threads 16  -preset ultrafast -hls_segment_filename D:\\test\\fb3a5850-2132-40c7-aa41-e98c3f4f429d_%05d.ts D:\\test\\fb3a5850-2132-40c7-aa41-e98c3f4f429d.m3u8";
    String cc = "-i D:\\test\\fb3a5850-2132-40c7-aa41-e98c3f4f429d12.ts -c copy -map 0 -f segment -segment_list D:\\bbb\\fb3a5850-2132-40c7-aa41-e98c3f4f429d2.m3u8 -segment_time 60 D:\\bbb\\fb3a5850-2132-40c7-aa41-e98c3f4f429d2.m3u8_%5d.ts";
    cmdExecut(aa);
        cmdExecut(cc);
        //异步执行,获取执行结果code
//        CompletableFuture<Integer> completableFutureTask = CompletableFuture.supplyAsync(() -> {
//            return cmdExecut(cc);
//        }, pool)
//                .thenApplyAsync((Integer code) -> {
//                    if (CODE_SUCCESS != code) {
//                        return CODE_FAIL;
//                    }
//                    System.out.println("第一步:mp4转mov,成功!");
          Integer codeTmp = cmdExecut(cmd_mov_water);
          if (CODE_SUCCESS != codeTmp) {
            return CODE_FAIL;
          }
//                    return 0;
//                }, pool);
//
//        //获取执行结果
//        //code=0表示正常
//        try {
//            System.out.println(String.format("获取最终执行结果:%s", completableFutureTask.get() == CODE_SUCCESS ? "成功!" : "失败!"));
//        } catch (InterruptedException e) {
//            Thread.currentThread().interrupt();
//            e.printStackTrace();
//        } catch (ExecutionException e) {
//            e.printStackTrace();
//        }

    }

    public static void createM3U8(String sourceFileUrl, String slicePath, String key) {
        //组装切片命令
        File file = new File(slicePath);
        if (!file.exists()) {
            file.mkdirs();
        }
        //切片文件存放路径-ts
        String sliceFileTsUrl = slicePath + key + "_%05d.ts";
        //切片文件存放路径-m3u8
        String sliceFileM3u8Url = slicePath + key + ".m3u8";
        String command = " -i  " + sourceFileUrl + " -hls_time 120 -hls_list_size 0 -threads 16  -preset ultrafast -hls_segment_filename " + sliceFileTsUrl + " " + sliceFileM3u8Url;
        //异步执行,获取执行结果code
        CompletableFuture<Integer> completableFutureTask = CompletableFuture.supplyAsync(() -> {
            return cmdExecut(command);
        }, pool)
                .thenApplyAsync((Integer code) -> {
                    if (0 != code) {
                        return 1;
                    }
                    return 0;
                }, pool);

        //获取执行结果
        //code=0表示正常
        try {
            log.info(String.format("获取最终执行结果:%s", completableFutureTask.get() == 0 ? "成功!" : "失败!"));
            if (completableFutureTask.get() == 0) {
                //切片成功
                log.info("视频文件切片成功!文件uuid为:" + sliceFileM3u8Url);
            } else {
                log.info("视频文件切片失败!文件uuid为:" + sliceFileM3u8Url);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException("视频切片失败");
        }
    }

//
//    public static void createM3U8(String sourceFileUrl, String slicePath, String key) {
//        try {
//        //组装切片命令
//        File file = new File(slicePath);
//        if (!file.exists()) {
//            file.mkdirs();
//        }
//        // 大ts文件名
//        String tsFile = slicePath+key+".ts";
//
//        //切片文件存放路径-ts
//        String sliceFileTsUrl = slicePath + key + "_%05d.ts";
//        //切片文件存放路径-m3u8
//        String sliceFileM3u8Url = slicePath + key + ".m3u8";
//        String aa = "-y -i " + sourceFileUrl +" -vcodec copy -acodec copy -vbsf h264_mp4toannexb "+tsFile;
//        String cc = "-i "+tsFile+" -c copy -map 0 -f segment -segment_list "+sliceFileM3u8Url+" -segment_time 120 "+sliceFileTsUrl;
//
//        Integer aatr=cmdExecut(aa);
//        if(aatr==0){
//            Integer cctr=cmdExecut(cc);
//            if(cctr!=0){
//                throw new ServiceException("视频切片失败");
//            }
//        }else {
//            throw new ServiceException("视频切片失败");
//        }
//
        String command = " -i  " + sourceFileUrl + " -hls_time 60 -hls_list_size 0 -threads 16  -preset ultrafast -hls_segment_filename " + sliceFileTsUrl + " " + sliceFileM3u8Url;
//        //异步执行,获取执行结果code
        CompletableFuture<Integer> completableFutureTask = CompletableFuture.supplyAsync(() -> {
            return cmdExecut(command);
        }, pool)
                .thenApplyAsync((Integer code) -> {
                    if (0 != code) {
                        return 1;
                    }
                    return 0;
                }, pool);

        //获取执行结果
        //code=0表示正常
        try {
            log.info(String.format("获取最终执行结果:%s", completableFutureTask.get() == 0 ? "成功!" : "失败!"));
            if (completableFutureTask.get() == 0) {
                //切片成功
                log.info("视频文件切片成功!文件uuid为:" + sliceFileM3u8Url);
            } else {
                log.info("视频文件切片失败!文件uuid为:" + sliceFileM3u8Url);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException("视频切片失败");
        }
//        } catch (Exception e) {
//            e.printStackTrace();
//            throw new ServiceException("视频切片失败");
//        }
//    }

    /**
     * @throws
     * @Description: (执行ffmpeg自定义命令)
     * @param: @param cmdStr
     * @param: @return
     * @return: Integer
     */
    public static Integer cmdExecut(String cmdStr) {
        //code=0表示正常
        Integer code = null;
        FfmpegCmd ffmpegCmd = new FfmpegCmd();
        /**
         * 错误流
         */
        InputStream errorStream = null;
        try {
            //destroyOnRuntimeShutdown表示是否立即关闭Runtime
            //如果ffmpeg命令需要长时间执行,destroyOnRuntimeShutdown = false

            //openIOStreams表示是不是需要打开输入输出流:
            //	       inputStream = processWrapper.getInputStream();
            //	       outputStream = processWrapper.getOutputStream();
            //	       errorStream = processWrapper.getErrorStream();
            ffmpegCmd.execute(false, true, cmdStr);
            errorStream = ffmpegCmd.getErrorStream();

            //打印过程
            int len = 0;
            while ((len = errorStream.read()) != -1) {
                System.out.print((char) len);
            }

            //code=0表示正常
            code = ffmpegCmd.getProcessExitCode();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            ffmpegCmd.close();
        }
        //返回
        return code;
    }

}


package com.fajiajia.meta.util;

import lombok.extern.log4j.Log4j2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ws.schild.jave.process.ProcessKiller;
import ws.schild.jave.process.ProcessWrapper;
import ws.schild.jave.process.ffmpeg.DefaultFFMPEGLocator;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * @Description:(cmd方式调用ffmpeg)
 */
@Log4j2
public class FfmpegCmd {

    private static final Logger LOG = LoggerFactory.getLogger(ProcessWrapper.class);

    /**
     * The process representing the ffmpeg execution.
     */
    private Process ffmpeg = null;

    /**
     * A process killer to kill the ffmpeg process with a shutdown hook, useful if the jvm execution
     * is shutted down during an ongoing encoding process.
     */
    private ProcessKiller ffmpegKiller = null;

    /**
     * A stream reading from the ffmpeg process standard output channel.
     */
    private InputStream inputStream = null;

    /**
     * A stream writing in the ffmpeg process standard input channel.
     */
    private OutputStream outputStream = null;

    /**
     * A stream reading from the ffmpeg process standard error channel.
     */
    private InputStream errorStream = null;

    /**
     * Executes the ffmpeg process with the previous given arguments.
     *
     * @param destroyOnRuntimeShutdown destroy process if the runtime VM is shutdown
     * @param openIOStreams            Open IO streams for input/output and errorout, should be false when
     *                                 destroyOnRuntimeShutdown is false too
     * @param ffmpegCmd                windows such as (mp4 transform to mov):
     *                                 " -i C:\\Users\\hsj\\AppData\\Local\\Temp\\jave\\honer.mp4 -c copy C:\\Users\\hsj\\AppData\\Local\\Temp\\jave\\honer_test.mov "
     * @throws IOException If the process call fails.
     */
    public void execute(boolean destroyOnRuntimeShutdown, boolean openIOStreams, String ffmpegCmd) throws IOException {
        DefaultFFMPEGLocator defaultFFMPEGLocator = new DefaultFFMPEGLocator();

        StringBuffer cmd = new StringBuffer(defaultFFMPEGLocator.getExecutablePath());
        //insert blank for delimiter
        cmd.append(" ");
        cmd.append(ffmpegCmd);
        String cmdStr = String.format("ffmpegCmd final is :%s", cmd.toString());
        System.out.println(cmdStr);
        LOG.info(cmdStr);

        Runtime runtime = Runtime.getRuntime();
        try {
            ffmpeg = runtime.exec(cmd.toString());

            if (destroyOnRuntimeShutdown) {
                ffmpegKiller = new ProcessKiller(ffmpeg);
                runtime.addShutdownHook(ffmpegKiller);
            }

            if (openIOStreams) {
                inputStream = ffmpeg.getInputStream();
                outputStream = ffmpeg.getOutputStream();
                errorStream = ffmpeg.getErrorStream();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Returns a stream reading from the ffmpeg process standard output channel.
     *
     * @return A stream reading from the ffmpeg process standard output channel.
     */
    public InputStream getInputStream() {
        return inputStream;
    }

    /**
     * Returns a stream writing in the ffmpeg process standard input channel.
     *
     * @return A stream writing in the ffmpeg process standard input channel.
     */
    public OutputStream getOutputStream() {
        return outputStream;
    }

    /**
     * Returns a stream reading from the ffmpeg process standard error channel.
     *
     * @return A stream reading from the ffmpeg process standard error channel.
     */
    public InputStream getErrorStream() {
        return errorStream;
    }

    /**
     * If there's a ffmpeg execution in progress, it kills it.
     */
    public void destroy() {
        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (Throwable t) {
                LOG.warn("Error closing input stream", t);
            }
            inputStream = null;
        }

        if (outputStream != null) {
            try {
                outputStream.close();
            } catch (Throwable t) {
                LOG.warn("Error closing output stream", t);
            }
            outputStream = null;
        }

        if (errorStream != null) {
            try {
                errorStream.close();
            } catch (Throwable t) {
                LOG.warn("Error closing error stream", t);
            }
            errorStream = null;
        }

        if (ffmpeg != null) {
            ffmpeg.destroy();
            ffmpeg = null;
        }

        if (ffmpegKiller != null) {
            Runtime runtime = Runtime.getRuntime();
            runtime.removeShutdownHook(ffmpegKiller);
            ffmpegKiller = null;
        }
    }

    /**
     * Return the exit code of the ffmpeg process If the process is not yet terminated, it waits for
     * the termination of the process
     *
     * @return process exit code
     */
    public int getProcessExitCode() {
        // Make sure it's terminated
        try {
            ffmpeg.waitFor();
        } catch (InterruptedException ex) {
            LOG.warn("Interrupted during waiting on process, forced shutdown?", ex);
        }
        return ffmpeg.exitValue();
    }

    /**
     * close
     **/
    public void close() {
        destroy();
    }

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值