Java调用ffmpeg把rtsp视频流保存为MP4文件,并播放
前言最近项目需要把rtsp的视频流截取保存为MP4文件。经过多方调研最终找到方案调用ffmpeg命令行完成转码。ffmpeg命令行确实稳定靠谱ok下面就开始分享代码。1、环境工具准备ffmpeg的程序和一个稳定的rtsp流这是我的ffmpeg的版本。我这里rtsp流用的海康硬盘录里的没有的同学可以网上找一些,有专门的网站提供一些免费的有一些流量的流比如rtsp://rtspstream:e06d5bdf15e5f992e39ebb33506c805ezephyr.rtsp.stream/moviertsp://rtspstream:742cb3f7bb397805f9e7829718fec9d8zephyr.rtsp.stream/pattern2、工具类代码说几个重点也是我踩了很久才出来的坑1.ffmpegPath一定要写绝对路径的地址(如 D:\ffmpeg\ffmpeg.exe).2.command在add的时候一定是一个字符一个位置不能直接拼成一句也不要在单独拼接空格。否则直接会抛出无效的指令3.获取的输入流必须单独建立一个线程来进行结果的打印如果直接转字节打印结束方法就会无效。4.输出流在次和dos发送指令时一定要刷新。核心处理类 RtspToMP4.javaimport 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: gaowh * create: 2024/4/24 10: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(); //定义命令内容 ListString command new ArrayList(); command.add(ffmpegPath); command.add(-y); command.add(-rtsp_transport); command.add(tcp); command.add(-i); command.add(streamUrl); // command.add(-c); command.add(-vcodec); 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; } }调用类 VideoController.javaimport com.ewaycloud.jw.cases.common.RtspToMP4; import com.ewaycloud.jw.common.core.util.R; 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.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.Map; /** * TODO: * * Author: gaowh * create: 2024/4/24 10:16 */ Api(tags 测试) RestController RequestMapping(/video) public class VideoController { Autowired private RtspToMP4 rtspToMP4; private MapInteger,Process mapnew HashMap(); ApiOperation(value 开始录制) GetMapping(value /start) public R star(RequestParam Integer caseId, RequestParam String streamUrl ) { //windows测试环境 String ffmpegPathD:\\ffmpeg\\ffmpeg-7.0-essentials_build\\bin\\ffmpeg.exe; String uploadPath F:\\upload\\; // streamUrlrtsp://admin:yw123456192.168.1.4:554/Streaming/Channels/801?transportmodeunicast; //linux生产环境 // String uploadPath /usr/local/upload/video/; //上传文件的名称 LocalDateTime now LocalDateTime.now(); DateTimeFormatter formatter DateTimeFormatter.ofPattern(yyyyMMddHHmmss); String fileName now.format(formatter).mp4; String filePath uploadPathfileName; Process process rtspToMP4.startRecord(ffmpegPath, streamUrl, filePath); if(null!process){ map.put(caseId,process); return R.ok(http://127.0.0.1/video/fileName); } return R.failed(caseId); } ApiOperation(value 结束录制) GetMapping(value /stop) public R stop(RequestParam Integer caseId) { if(map.containsKey(caseId)){ Process process map.get(caseId); if(null!process){ rtspToMP4.stopRecord(process); return R.ok(caseId); } } return R.failed(); } }3、postman调用测试先调用开始截流再调用结束接口不然会一直录4、调用结束接口5、我这里配了Nginx将f:\\upload指向了videoworker_processes 1; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; #gzip on; server { listen 80; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { root html; index index.html index.htm; } location /file { alias F:\upload; allow all; autoindex on; } location /video{ alias F:\upload; allow all; autoindex on; } error_page 500 502 503 504 /50x.html; location /50x.html { root html; } } server { listen 8088; server_name localhost; gzip on; gzip_static on; gzip_min_length 1k; gzip_comp_level 4; gzip_proxied any; gzip_types text/plain text/xml text/css; gzip_vary on; gzip_disable MSIE [1-6]\.(?!.*SV1); root E:\workspace\jiweijk\代码\ewaycloud-jw-ui\dist; location ~* ^/(ewaycloud/ws) { proxy_pass http://127.0.0.1:8000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; } location ~* ^/(ewaycloud) { proxy_pass http://127.0.0.1:8000; proxy_connect_timeout 15s; proxy_send_timeout 15s; proxy_read_timeout 15s; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto http; } location ~* ^/(gen) { proxy_pass http://127.0.0.1:5003; proxy_connect_timeout 15s; proxy_send_timeout 15s; proxy_read_timeout 15s; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto http; } if ($request_uri ~ /actuator){ return 403; } } }6、看是否已经截取出了mp4视频http://127.0.0.1/video/20240424160858.mp47、大功告成分享完毕欢迎各位观众老爷一键三连感兴趣的小伙伴也可以加我微信交流微信在文章的末尾~