一、常用查看指令
1.查看FFmpeg支持的编码器
ffmpeg configure -encoders
2.查看FFmpeg支持的编码器
ffmpeg configure -decoders
3.查看ffmpeg支持的通信协议
ffmpeg configure -protocols
4.查看FFmpeg所支持的音视频编码格式、文件封装格式与流媒体传输协议
ffmpeg configure --help
二、常用操作视频命令
1.视频压缩
ffmpeg -i ahaha.mp4 -vcodec h264 -vf scale=640:-2 -threads 4 2020_conv.mp4
ffmpeg -i ahaha.mp4 -strict -2 -vcodec h264 1579251906_output.mp4
-i ahaha.mp4
输入文件,源文件
xy_conv.mp4
输出文件,目标文件
-vf scale=640:-2
改变视频分辨率,缩放到640px宽,高度的-2是考虑到libx264要求高度是偶数,所以设置成-2,让软件自动计算得出一个接近等比例的偶数高
-threads 4
4核运算
相关参数
-s 1280x720
设置输出文件的分辨率,w*h。
-b:v
输出文件的码率,一般500k左右即可,人眼看不到明显的闪烁,这个是与视频大小最直接相关的。
-preset
指定输出的视频质量,会影响文件的生成速度,有以下几个可用的值 ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow。
与 veryslow相比,placebo以极高的编码时间为代价,只换取了大概1%的视频质量提升。这是一种收益递减准则:slow 与 medium相比提升了5%~10%;slower 与 slow相比提升了5%;veryslow 与 slower相比提升了3%。
针对特定类型的源内容(比如电影、动画等),还可以使用-tune参数进行特别的优化。
-an
去除音频流。
-vn
去除视频流。
-c:a
指定音频编码器。
-c:v
指定视频编码器,libx264,libx265,H.262,H.264,H.265。
libx264:最流行的开源 H.264 编码器。
NVENC:基于 NVIDIA GPU 的 H.264 编码器。
libx265:开源的 HEVC 编码器。
libvpx:谷歌的 VP8 和 VP9 编码器。
libaom:AV1 编码器。
-vcodec copy
表示不重新编码,在格式未改变的情况采用。
-re
以源文件固有帧率发送数据。
-minrate 964K -maxrate 3856K -bufsize 2000K
指定码率最小为964K,最大为3856K,缓冲区大小为 2000K。
-y
不经过确认,输出时直接覆盖同名文件。
-crf
参数来控制转码,取值范围为 0~51,其中0为无损模式,18~28是一个合理的范围,数值越大,画质越差。
2.视频拼接
1.将4个视频拼接成一个很长的视频(无声音)
ffmpeg -i 0.mp4 -i 1.mp4 -i 2.mp4 -i 3.mp4 -filter_complex '[0:0][1:0] [2:0][3:0] concat=n=4:v=1 [v]' -map '[v]' output.mp4
2.将4个视频拼接成一个很长的视频(有声音)
ffmpeg -i 1.mp4 -i 2.mp4 -i 3.mp4 -filter_complex '[0:0][0:1] [1:0][1:1] [2:0][2:1] concat=n=3:v=1:a=1 [v][a]' -map '[v]' -map '[a]’ output.mp4
[0:0][0:1] [1:0][1:1] [2:0][2:1]
分别表示第1个输入文件的视频、音频,第2个输入文件的视频、音频,第3个输入文件的视频、音频。
concat=n=3:v=1:a=1
表示有3个输入文件,输出一条视频流和一条音频流。
[v][a]
得到的视频流和音频流的名字,注意在 bash 等 shell 中需要用引号,防止通配符扩展。
3.横向拼接视频2个
ffmpeg -i 0.mp4 -i 1.mp4 -filter_complex "[0:v]pad=iw*2:ih*1[a];[a][1:v]overlay=w" out.mp4
pad
将合成的视频宽高,这里iw代表第1个视频的宽,iw*2代表合成后的视频宽度加倍,ih为第1个视频的高,合成的两个视频最好分辨率一致。
overlay
覆盖,[a][1:v]overlay=w,后面代表是覆盖位置w:0。
4.竖向拼接视频2个
ffmpeg -i 0.mp4 -i 1.mp4 -filter_complex "[0:v]pad=iw:ih*2[a];[a][1:v]overlay=0:h" out_2.mp4
5.横向拼接视频3个
ffmpeg -i 0.mp4 -i 1.mp4 -i 2.mp4 -filter_complex "[0:v]pad=iw*3:ih*1[a];[a][1:v]overlay=w[b];[b][2:v]overlay=2.0*w" out_v3.mp4
6.竖向拼接视频3个
ffmpeg -i 0.mp4 -i 1.mp4 -i 2.mp4 -filter_complex "[0:v]pad=iw:ih*3[a];[a][1:v]overlay=0:h[b];[b][2:v]overlay=0:2.0*h" out_v4.mp4
7.竖向拼接视频4个品字形
ffmpeg -i 0.mp4 -i 1.mp4 -i 2.mp4 -i 3.mp4 -filter_complex "[0:v]pad=iw*2:ih*2[a];[a][1:v]overlay=w[b];[b][2:v]overlay=0:h[c];[c][3:v]overlay=w:h" out.mp4
3.截取视频第一帧或者某一帧
# input seeking
ffmpeg -ss 00:1:05 -i gemfield.mp4 -frames:v 1 out.jpg
# output seeking
ffmpeg -i gemfield.mp4 -ss 00:1:05 -frames:v 1 out1.jpg
-frame:v 1,在video stream上截取1帧。
input seeking使用的是key frames,所以速度很快;而output seeking是逐帧decode,直到1分05秒,所以速度很慢。
ffmpeg截取视频帧有2种 seeking 方式,对应有2种 coding 模式:transcoding 和 stream copying(ffmpeg -c copy)。
transcoding 模式:需要 decoding + encoding 的模式,即先 decoding 再encoding。
stream copying 模式:不需要decoding + encoding的模式,由命令行选项-codec加上参数copy来指定(-c:v copy )。在这种模式下,ffmpeg在video stream上就会忽略 decoding 和 encoding步骤。
查看视频总帧数
ffprobe -v error -count_frames -select_streams v:0 -show_entries stream=nb_frames -of default=nokey=1:noprint_wrappers=1 gemfield.mp4
4.图片转视频
ffmpeg -f image2 -i 'in%6d.jpg' -vcodec libx264 -r 25 -b 200k test.mp4
-r 25 表示每秒播放25帧
-b 200k 指定码率为200k
图片的文件名为"in000000.jpg",从0开始依次递增。
4.图片格式转化
1.webp转换成jpg
ffmpeg -i in.webp out.jpg
2.webp转换成png
ffmpeg -i in.webp out.png
3.jpg转换成png
ffmpeg -i in.jpg out.png
4.jpg转换成webp
ffmpeg -i in.jpg out.webp
5.png转换成webp
ffmpeg -i in.png out.webp
6.png转换成jpg
ffmpeg -i in.png out.jpg
三、java操作ffmpeg录制视频
1.相关代码
/**
*
* @Author: xy丶
* @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, "UTF-8");
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;
}
/**
* 停止录制
* @param process
* @return
*/
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;
}
/**
* 音视频合并,视频结束,音频结束 -- (cmd(windows): ffmpeg.exe -i test2.mp3 -i test1.mp4 -t 10 -y newVideo.mp4)
*
* @param ffmpegPath ffmpeg.exe文件路径,可在rest或者admin中进行配置,使用配置文件进行读取
* @param audioInputPath 音频文件路径(输入)
* @param videoInputPath 视频文件路径(输入)
* @param time 文件时长
* @param videoOutputPath 转换完成的文件路径(输出)
* @throws IOException
*/
public static void audioVideoMerge(String ffmpegPath, String audioInputPath, String videoInputPath, double time, String videoOutputPath) throws IOException {
// 构建命令
List<String> command = Lists.newArrayList();
command.add(ffmpegPath);
command.add("-i");
command.add(audioInputPath);
command.add("-i");
command.add(videoInputPath);
command.add("-t");
command.add(String.valueOf(time));
command.add("-y");
command.add(videoOutputPath);
// 执行操作
ProcessBuilder builder = new ProcessBuilder(command);
Process process = builder.start();
InputStream errorStream = process.getErrorStream();
InputStreamReader isr = new InputStreamReader(errorStream);
BufferedReader br = new BufferedReader(isr);
String line = "";
while ((line = br.readLine()) != null) {
}
if (br != null) {
br.close();
}
if (isr != null) {
isr.close();
}
if (errorStream != null) {
errorStream.close();
}
}
/* public void testAudioVideoMerge() {
FfmpegProperties ffmpegProperties = SpringContextHolder.getBean(FfmpegProperties.class);
try {
FfmpegUtil.audioVideoMerge(ffmpegProperties.getFfmpegFile(), "D:\\tools\\ffmpeg\\bin\\test2.mp3", "D:\\tools\\ffmpeg\\bin\\test1.mp4", 10, "D:\\tools\\ffmpeg\\bin\\newVideo.mp4");
} catch (IOException e) {
e.printStackTrace();
}
}*/
}
2.开始录制
private Map<Integer,Process> map=new HashMap<>();
/**
* 开始录制
* @param id
* @param FileName
* @return
*/
@GetMapping(value = "/startRecord")
public Result<Object> Start(Integer id, String FileName) {
String ffmpegPath="E:\\install\\ffmpeg\\bin\\ffmpeg.exe";
//rtsp://127.0.0.1:554/rtp/34020000001110000001_34020000001320000002 rtsp://127.0.0.1:554/rtp/44010200492000000001_34020000001320000001
String streamUrl="rtsp://127.0.0.1:554/rtp/44010200492000000001_34020000001320000001";
String FilePath="E:\\ffmp4\\"+FileName;
Process process = rtspToMP4.startRecord(ffmpegPath, streamUrl, FilePath);
if(null!=process){
map.put(id,process);
boolean interceptPhoto = VedioUtils.interceptPhoto(ffmpegPath, streamUrl, "E:\\ffmp4\\rtsp新脚本.jpg");
if (!interceptPhoto){
return Results.newFailedResult(ErrorCodeEnum.UNKNOWN);
}
return Results.newSuccessResult("操作成功");
}
return Results.newFailedResult(ErrorCodeEnum.UNKNOWN);
}
3.结束录制
/**
* 结束录制
* @param id
* @return
*/
@GetMapping(value = "/stop")
public Result<Object> stop(Integer id) {
if(map.containsKey(id)){
Process process = map.get(id);
if(null!=process){
rtspToMP4.stopRecord(process);
return Results.newSuccessResult("操作成功");
}
}
return Results.newFailedResult(ErrorCodeEnum.UNKNOWN);
}
4.合并视频
/**
* @Description:合并视频
* @Author: xy丶
*/
@GetMapping("/mergeVideo")
public void mergeVideo( HttpServletResponse response){
List<String> paths = Lists.newArrayList();
paths.add("E:\\excel\\test.mp4");
paths.add("E:\\excel\\test1.mp4");
paths.add("E:\\excel\\test2.mp4");
paths.add("E:\\excel\\test3.mp4");
CMDExecteUtil excutor = new CMDExecteUtil();
//String cmd = VedioUtils.mergeCmd[paths.size() - 1];
String cmd = "ffmpeg %s -filter_complex \"nullsrc=size=640x480[base];[0:v]setpts=PTS-STARTPTS,scale=320x240[1];[1:v]setpts=PTS-STARTPTS,scale=320x240[2];[2:v]setpts=PTS-STARTPTS,scale=320x240[3];[3:v]setpts=PTS-STARTPTS,scale=320x240[4];[base][1]overlay=shortest=1[tmp1];[tmp1][2]overlay=shortest=1:x=320[tmp2];[tmp2][3]overlay=shortest=1:y=240[tmp3];[tmp3][4]overlay=shortest=1:x=320:y=240\" %s";
StringBuilder sb = new StringBuilder();
for (String s : paths) {
if (StringUtils.isEmpty(sb)) {
sb.append("-i ").append(s);
} else {
sb.append(" ").append("-i ").append(s);
}
}
// "ffmpeg %s -filter_complex \"nullsrc=size=640x480[base];[0:v]setpts=PTS-STARTPTS,scale=320x240[1];[1:v]setpts=PTS-STARTPTS,scale=320x240[2];[2:v]setpts=PTS-STARTPTS,scale=320x240[3];[3:v]setpts=PTS-STARTPTS,scale=320x240[4];[base][1]overlay=shortest=1[tmp1];[tmp1][2]overlay=shortest=1:x=320[tmp2];[tmp2][3]overlay=shortest=1:y=240[tmp3];[tmp3][4]overlay=shortest=1:x=320:y=240\" %s",
String tmpCMD = String.format(cmd, sb.toString(), "E:\\excel\\mergeVideo.mp4");
excutor.exec(tmpCMD);
}
public class CMDExecteUtil {
Logger logger = LoggerFactory.getLogger(CMDExecteUtil.class);
/**
* 执行命令
* @param command 命令语句
* @return
*/
public String exec(String command) {
try {
//创建子进程执行命令语句
Process p = Runtime.getRuntime().exec(command);
StreamCaptureThread errorStream = new StreamCaptureThread(p.getErrorStream());
StreamCaptureThread outputStream = new StreamCaptureThread(p.getInputStream());
new Thread(errorStream).start();
new Thread(outputStream).start();
//等待执行完毕
p.waitFor();
String result = command + "\n" + outputStream.output.toString() + errorStream.output.toString();
logger.info(result);
if (!StringUtils.isEmpty(errorStream.output.toString())) {
return errorStream.output.toString();
}
} catch (Exception e) {
logger.error(e.getMessage());
}
return null;
}
private class StreamCaptureThread implements Runnable {
InputStream stream;
StringBuilder output;
public StreamCaptureThread(InputStream stream) {
this.stream = stream;
this.output = new StringBuilder();
}
@Override
public void run() {
try {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(this.stream));
String line = br.readLine();
while (line != null) {
if (line.trim().length() > 0) {
output.append(line).append("\n");
}
line = br.readLine();
}
} finally {
if (stream != null) {
stream.close();
}
}
} catch (IOException ex) {
ex.printStackTrace(System.err);
}
}
}
}
5.其他操作
/**
* @author xy丶
* @date 2023/10/11
*/
@Service
@Slf4j
public class FFmpegServiceImpl {
//九宫格命令
private String[] mergeCmd = {
"ffmpeg %s -filter_complex \"nullsrc=size=640x480[base];[0:v]setpts=PTS-STARTPTS,scale=640x480[1];[base][1]overlay=shortest=1\" %s",
"ffmpeg -i 1.mp4 -i 2.mp4 -filter_complex \"nullsrc=size=640x480[base];[0:v]setpts=PTS-STARTPTS,scale=320x480[1];[1:v]setpts=PTS-STARTPTS,scale=320x480[2];[base][1]overlay=shortest=1[tmp1];[tmp1][2]overlay=shortest=1:x=320\" mergeOut.mp4",
"ffmpeg -i 1.mp4 -i 2.mp4 -i 3.mp4 -filter_complex \"nullsrc=size=640x480[base];[0:v]setpts=PTS-STARTPTS,scale=320x240[1];[1:v]setpts=PTS-STARTPTS,scale=320x240[2];[2:v]setpts=PTS-STARTPTS,scale=320x240[3];[base][1]overlay=shortest=1[tmp1];[tmp1][2]overlay=shortest=1:y=240[tmp2];[tmp2][3]overlay=shortest=1:x=320\" mergeOut.mp4",
"ffmpeg -i 1.mp4 -i 2.mp4 -i 3.mp4 -i 3.mp4 -i 3.mp4 -filter_complex \"nullsrc=size=640x480[base];[0:v]setpts=PTS-STARTPTS,scale=320x240[1];[1:v]setpts=PTS-STARTPTS,scale=320x240[2];[2:v]setpts=PTS-STARTPTS,scale=320x240[3];[3:v]setpts=PTS-STARTPTS,scale=320x240[4];[base][1]overlay=shortest=1[tmp1];[tmp1][2]overlay=shortest=1:x=320[tmp2];[tmp2][3]overlay=shortest=1:y=240[tmp3];[tmp3][4]overlay=shortest=1:x=320:y=240\" mergeOut.mp4",
"ffmpeg -i 1.mp4 -i 2.mp4 -i 3.mp4 -i 4.mp4 -i 5.mp4 -filter_complex \"nullsrc=size=690x480[base];[0:v]setpts=PTS-STARTPTS,scale=230x240[1];[1:v]setpts=PTS-STARTPTS,scale=230x240[2];[2:v]setpts=PTS-STARTPTS,scale=230x240[3];[3:v]setpts=PTS-STARTPTS,scale=230x240[4];[4:v]setpts=PTS-STARTPTS,scale=230x240[5];[base][1]overlay=shortest=1[tmp1];[tmp1][2]overlay=shortest=1:x=230[tmp2];[tmp2][3]overlay=shortest=1:x=460[tmp3];[tmp3][4]overlay=shortest=1:y=240[tmp4];[tmp4][5]overlay=shortest=1:x=230:y=240\" mergeOut.mp4",
"ffmpeg -i 1.mp4 -i 2.mp4 -i 3.mp4 -i 4.mp4 -i 5.mp4 -i 6.mp4 -filter_complex \"nullsrc=size=690x480[base];[0:v]setpts=PTS-STARTPTS,scale=230x240[1];[1:v]setpts=PTS-STARTPTS,scale=230x240[2];[2:v]setpts=PTS-STARTPTS,scale=230x240[3];[3:v]setpts=PTS-STARTPTS,scale=230x240[4];[4:v]setpts=PTS-STARTPTS,scale=230x240[5];[5:v]setpts=PTS-STARTPTS,scale=230x240[6];[base][1]overlay=shortest=1[tmp1];[tmp1][2]overlay=shortest=1:x=230[tmp2];[tmp2][3]overlay=shortest=1:x=460[tmp3];[tmp3][4]overlay=shortest=1:y=240[tmp4];[tmp4][5]overlay=shortest=1:x=230:y=240[tmp5];[tmp5][6]overlay=shortest=1:x=460:y=240\" mergeOut.mp4",
"ffmpeg -i 1.mp4 -i 2.mp4 -i 3.mp4 -i 4.mp4 -i 5.mp4 -i 6.mp4 -i 7.mp4 -filter_complex \"nullsrc=size=690x480[base];[0:v]setpts=PTS-STARTPTS,scale=230x160[1];[1:v]setpts=PTS-STARTPTS,scale=230x160[2];[2:v]setpts=PTS-STARTPTS,scale=230x160[3];[3:v]setpts=PTS-STARTPTS,scale=230x160[4];[4:v]setpts=PTS-STARTPTS,scale=230x160[5];[5:v]setpts=PTS-STARTPTS,scale=230x160[6];[6:v]setpts=PTS-STARTPTS,scale=230x160[7];[base][1]overlay=shortest=1[tmp1];[tmp1][2]overlay=shortest=1:x=230[tmp2];[tmp2][3]overlay=shortest=1:x=460[tmp3];[tmp3][4]overlay=shortest=1:y=160[tmp4];[tmp4][5]overlay=shortest=1:x=230:y=160[tmp5];[tmp5][6]overlay=shortest=1:x=460:y=160[tmp6];[tmp6][7]overlay=shortest=1:y=320\" mergeOut.mp4",
"ffmpeg -i 1.mp4 -i 2.mp4 -i 3.mp4 -i 4.mp4 -i 5.mp4 -i 6.mp4 -i 7.mp4 -i 8.mp4 -filter_complex \"nullsrc=size=690x480[base];[0:v]setpts=PTS-STARTPTS,scale=230x160[1];[1:v]setpts=PTS-STARTPTS,scale=230x160[2];[2:v]setpts=PTS-STARTPTS,scale=230x160[3];[3:v]setpts=PTS-STARTPTS,scale=230x160[4];[4:v]setpts=PTS-STARTPTS,scale=230x160[5];[5:v]setpts=PTS-STARTPTS,scale=230x160[6];[6:v]setpts=PTS-STARTPTS,scale=230x160[7];[7:v]setpts=PTS-STARTPTS,scale=230x160[8];[base][1]overlay=shortest=1[tmp1];[tmp1][2]overlay=shortest=1:x=230[tmp2];[tmp2][3]overlay=shortest=1:x=460[tmp3];[tmp3][4]overlay=shortest=1:y=160[tmp4];[tmp4][5]overlay=shortest=1:x=230:y=160[tmp5];[tmp5][6]overlay=shortest=1:x=460:y=160[tmp6];[tmp6][7]overlay=shortest=1:y=320[tmp7];[tmp7][8]overlay=shortest=1:x=230:y=320\" mergeOut.mp4",
"ffmpeg -i 1.mp4 -i 2.mp4 -i 3.mp4 -i 4.mp4 -i 5.mp4 -i 6.mp4 -i 7.mp4 -i 8.mp4 -i 9.mp4 -filter_complex \"nullsrc=size=690x480[base];[0:v]setpts=PTS-STARTPTS,scale=230x160[1];[1:v]setpts=PTS-STARTPTS,scale=230x160[2];[2:v]setpts=PTS-STARTPTS,scale=230x160[3];[3:v]setpts=PTS-STARTPTS,scale=230x160[4];[4:v]setpts=PTS-STARTPTS,scale=230x160[5];[5:v]setpts=PTS-STARTPTS,scale=230x160[6];[6:v]setpts=PTS-STARTPTS,scale=230x160[7];[7:v]setpts=PTS-STARTPTS,scale=230x160[8];[8:v]setpts=PTS-STARTPTS,scale=230x160[9];[base][1]overlay=shortest=1[tmp1];[tmp1][2]overlay=shortest=1:x=230[tmp2];[tmp2][3]overlay=shortest=1:x=460[tmp3];[tmp3][4]overlay=shortest=1:y=160[tmp4];[tmp4][5]overlay=shortest=1:x=230:y=160[tmp5];[tmp5][6]overlay=shortest=1:x=460:y=160[tmp6];[tmp6][7]overlay=shortest=1:y=320[tmp7];[tmp7][8]overlay=shortest=1:x=230:y=320[tmp8];[tmp8][9]overlay=shortest=1:x=460:y=320\" mergeOut.mp4",
};
//剪切命令
private String shearCmd = "ffmpeg -i %s -ss 0 -c copy -t %s -codec copy %s";
//拼接命令
private String jointCmd = "ffmpeg -f concat -safe 0 -i %s -c copy %s";
//提取一帧转为图片
private String cropCmd ="ffmpeg -i %s -frames:v 1 %s";
//剪辑固定时长视频
public void shearVideo(String source, Long dur, String outputName) {
dur = dur / 1000;
outputName = outputName.replace(":", "");
String outputPath = source + outputName;
String tmpCMD = String.format(shearCmd, source + "tmp.mp4", dur, outputPath);
CMDExecteUtil excutor = new CMDExecteUtil();
excutor.exec(tmpCMD);
}
//拼接多个视频
public void jointVideo(String filePath, String outPath) {
String tmpCMD = String.format(jointCmd, filePath, outPath);
CMDExecteUtil excutor = new CMDExecteUtil();
excutor.exec(tmpCMD);
}
//合并多个视频
public void mergeVideo(List<String> paths, String outPutPath) {
String cmd = mergeCmd[paths.size() - 1];
String viPath = "";
StringBuilder sb = new StringBuilder();
for (String s : paths) {
if (StringUtils.isEmpty(sb)) {
sb.append("-i ").append(s);
} else {
sb.append(" ").append("-i ").append(s);
}
}
String tmpCMD = String.format(cmd, sb.toString(), outPutPath + "merge.mp4");
CMDExecteUtil excutor = new CMDExecteUtil();
excutor.exec(tmpCMD);
}
//截取一帧
public void cropImage(String streamPath,String outPath){
String tmpCMD = String.format(cropCmd, streamPath, outPath);
log.info("FFMPEG---"+tmpCMD);
CMDExecteUtil excutor = new CMDExecteUtil();
excutor.exec(tmpCMD);
}
}
结尾:干活满满,喜欢就点个赞收藏吧