java调用ffmpeg把rtsp视频流保存为MP4文件

前言:最近需要把rtsp的视频流保存为MP4文件(就是录制直播流)。刚开始用的javacv的FFmpegFrameGrabber和FFmpegFrameRecorder,但是声音流和视频流无法调整,声音和视频一直对不上而且录制的视频也有问题每次录10秒保存的视频却有18秒。毕竟不是专业做音视频的,很多东西也不了解,也是一步步摸索。最后也是向位大佬询问,也给我了很好建议:1.是调用ffmpeg命令行完成转码。2.增加中间件srs,使用srs拉去rtsp流数据并录制为ts文件,再使用java调用ffmpeg将ts列表合成文件。大佬比较建议使用第一种方案,因为srs的Dvr录制不稳定。ffmpeg命令行稳定点。ok,下面就开始分享代码。

  1. 环境工具准备
    1. ffmpeg的程序和一个稳定的rtsp流
    这是我的ffmpeg的版本。
    fmpeg version git-2019-10-11-71d9ae1 Copyright (c) 2000-2019 the FFmpeg developers built with gcc 9.2.1 (GCC) 20191010

  2. 工具类代码,说几个重点也是我踩了很久才出来的坑:
    1.ffmpegPath一定要写绝对路径的地址(如 D:\ffmpeg\ffmpeg.exe).
    2.command在add的时候一定是一个字符一个位置不能直接拼成一句,也不要在单独拼接空格。否则直接会抛出无效的指令
    3.获取的输入流必须单独建立一个线程来进行结果的打印,如果直接转字节打印,结束方法就会无效。
    4.输出流在次和dos发送指令时一定要刷新。

    import org.springframework.stereotype.Component;
    
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.text.SimpleDateFormat;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    
    /**
     * TODO:
     *
     * @Author: ZHANG
     * @create: 2021/8/27 16:11
     */
    @Component
    public class RtspToMP4 {
    
        public class In implements Runnable{
            private InputStream inputStream;
    
            public In(InputStream inputStream) {
                this.inputStream = inputStream;
            }
            @Override
            public void run() {
                try {
                    //转成字符输入流
                    InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "gbk");
                    int len = -1;
                    char[] c = new char[1024];
                    //读取进程输入流中的内容
                    while ((len = inputStreamReader.read(c)) != -1) {
                        String s = new String(c, 0, len);
                        System.out.print(s);
                    }
                }catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
        public Process StartRecord(String ffmpegPath,String streamUrl, String FilePath){
            ProcessBuilder processBuilder = new ProcessBuilder();
            //定义命令内容
            List<String> command = new ArrayList<>();
            command.add(ffmpegPath);
            command.add("-rtsp_transport");
            command.add("tcp");
            command.add("-y");
            command.add("-i");
            command.add(streamUrl);
            command.add("-c");
            command.add("copy");
            command.add("-f");
            command.add("mp4");
            command.add(FilePath);
            processBuilder.command(command);
            System.out.println("脚本:" + command.toString());
            //将标准输入流和错误输入流合并,通过标准输入流读取信息
            processBuilder.redirectErrorStream(true);
            try {
                //启动进程
                Process process = processBuilder.start();
                System.out.println("开始时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(System.currentTimeMillis())));
                //获取输入流
                InputStream inputStream = process.getInputStream();
                Thread inThread = new Thread(new In(inputStream));
                inThread.start();
               return process;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
        public boolean stopRecord(Process process) {
            try {
                OutputStream os = process.getOutputStream();
                os.write("q".getBytes());
                // 一定要刷新
                os.flush();
                os.close();
            } catch (Exception err) {
                err.printStackTrace();
                return false;
            }
            return true;
        }
    }
    
  3. 在贴一下调用的代码

    import com.beiLinCourt.common.result.Result;
    import com.beiLinCourt.common.task.RtspToMP4;
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * TODO:
     *
     * @Author: ZHANG
     * @create: 2021/8/27 16:16
     */
    @Api(tags = "测试")
    @RestController
    @RequestMapping("/test")
    public class TestController {
        @Autowired
        private RtspToMP4 rtspToMP4;
    
        private Map<Integer,Process> map=new HashMap<>();
    
        @ApiOperation(value = "开始录制")
        @GetMapping(value = "/Start")
        public Result<String> Start(Integer id,String FileName) {
            String ffmpegPath="D:\ffmpeg\ffmpeg.exe";
            String streamUrl="rtsp://192.168.0.168/0";
            String FilePath="E:\\data\\MP4\\"+FileName;
            Process process = rtspToMP4.StartRecord(ffmpegPath, streamUrl, FilePath);
            if(null!=process){
                map.put(id,process);
                return Result.success();
            }
           return Result.failed();
        }
    
        @ApiOperation(value = "结束录制")
        @GetMapping(value = "/stop")
        public Result<String> stop(Integer id) {
            if(map.containsKey(id)){
                Process process = map.get(id);
                if(null!=process){
                    rtspToMP4.stopRecord(process);
                    return Result.success();
                }
            }
            return Result.failed();
        }
    }
    
  4. 启动项目,进入swagger测试
    java调用ffmpeg把rtsp视频流保存为MP4文件_第1张图片
    控制台日志
    java调用ffmpeg把rtsp视频流保存为MP4文件_第2张图片
    在调用结束接口
    java调用ffmpeg把rtsp视频流保存为MP4文件_第3张图片
    在这里插入图片描述
    在看一下录制的视频文件
    在这里插入图片描述
    java调用ffmpeg把rtsp视频流保存为MP4文件_第4张图片
    视频时间也对,声音的输出也正常。暂时也没有发现其他的什么Bug。

  5. 最后也是感谢大佬,给我提供了一个很好的思路。最后有什么错误的地方请指出,大家一起讨论,一起进步。

你可能感兴趣的:(java调用ffmpeg,java,rtsp,ffmpeg)