java调用ffmpeg录屏时,启动比较容易,麻烦的是结束,直接kill ffmpeg.exe进程会导致录屏失败。另外,可能更多的用法是通过http请求来调用录屏的开始和结束方法。故此我做了封装,将调用ffmpeg录屏的开始和结束分为两个方法,开始方法的输入是完整的cmd命令,包含ffmpeg.exe的位置、参数、输出视频的路径等,根据需要可自行修改,结束方法不需要任何参数。
另外还包含了给视频添加水印方法,由于是一帧一帧的添加文字水印,因此添加水印方法会非常慢,生产环境慎用。
源码如下:
package com.ls.ffmpeg.controller;
import com.ls.ffmpeg.foundation.VideoWatermarkUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
/**
* Created with IntelliJ IDEA.
*
* @ClassName: FfmpegController
* @Description:
* @Author: LS
* @Date: 2023/8/2 14:50
*/
@RestController
@RequestMapping("/ffmpeg")
@Api(tags = "FFmpeg管理")
public class FfmpegController {
@Resource
VideoWatermarkUtil videoWatermarkUtil;
private Runtime runtime;
private Process pro;
/**
* @Description: 开始录屏。完整参数如下所示,命令中的每个属性配置需要自己去查相关含义,本人也不是很懂
* @Param: [cmd]
* @Return: java.lang.String
* @Author: LS
* @Date: 2023/8/9 9:53
*/
@ApiOperation("开始录屏")
@PostMapping("/start")
@ApiImplicitParams({
@ApiImplicitParam(name = "cmd", value = "windows调用ffmpeg的完整录屏命令", dataType = "String",required = true)
})
public String start(String cmd){
//String cmd="E:\\software\\录屏\\ffmpeg\\bin\\ffmpeg.exe -f gdigrab -framerate 30 -i desktop -c:v libx264 -preset slower -crf 18 -pix_fmt yuv420p -s 1920x1080 C:\\test.mp4";
new Thread(()->exec(cmd)).start();
return "success";
}
private void exec(String cmd) {
try {
System.out.println("系统开始执行命令:\n" + cmd);
runtime = Runtime.getRuntime();
pro = runtime.exec(cmd);
InputStream stream = pro.getErrorStream();
output(stream);
pro.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
}
@ApiOperation("结束录屏")
@PostMapping("/stop")
public String stop() throws IOException {
OutputStream ostream = pro.getOutputStream();
ostream.write("q\r\n".getBytes());
ostream.flush();
ostream.close();
return "success";
}
public void output(InputStream stream) {
InputStreamReader isr = null;
BufferedReader br = null;
try {
isr = new InputStreamReader(stream, "gbk");
br = new BufferedReader(isr);
String str;
// 通过readLine()方法按行读取字符串
while ((str = br.readLine()) != null) {
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
close(br, isr, stream);
}
}
public void close(AutoCloseable ... acs) {
for(AutoCloseable ac : acs) {
try {
ac.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* @Description: 给现有视频添加水印。原理是一帧一帧的添加水印文字,故此该执行方法非常慢。另外其他字体、加粗等属性,可在方法中自行配置
* @Param: [orgFilePathWithFileName, outFilePathWithFileName, drawString, size, x, y]
* @Return: java.lang.String
* @Author: LS
* @Date: 2023/8/2 9:44
*/
@ApiOperation("添加水印")
@PostMapping("/addWatermark")
@ApiImplicitParams({
@ApiImplicitParam(name = "orgFilePathWithFileName", value = "原始完整文件路径(包含文件名)", dataType = "String",required = true),
@ApiImplicitParam(name = "outFilePathWithFileName", value = "输出的完整文件路径(包含文件名)", dataType = "String",required = true),
@ApiImplicitParam(name = "drawString", value = "要添加的水印文字", dataType = "String",required = true),
@ApiImplicitParam(name = "size", value = "字体大小", dataType = "int",required = true),
@ApiImplicitParam(name = "x", value = "位置,以屏幕左上角为原点", dataType = "int",required = true),
@ApiImplicitParam(name = "y", value = "位置,以屏幕左上角为原点", dataType = "int",required = true)
})
public String addWatermark(String orgFilePathWithFileName,String outFilePathWithFileName,String drawString,int size,int x,int y) {
try{
videoWatermarkUtil.addVideoWatermark(orgFilePathWithFileName, outFilePathWithFileName, drawString, size, x, y);
}catch (Exception e){
return "error";
}
return "success";
}
}
完整的项目地址见码云: