网上大多是通过命令的显示操作ffmpeg,不太灵活
这里我给出剪切视频的一段代码,缺点是无法精确,比如我想剪切20秒,但是可能剪切好的是23秒。
其中FFmpegFrameRecorder(outputstream)操作流会报错。原因未知。
import cn.hutool.core.io.FileUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.util.MapUtil;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.*;
import org.opencv.video.Video;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.;
import java.security.SecureRandom;
import java.util.;
@Slf4j
public class VideoUtils {
public static void main(String[] args) throws Exception {
// t1();
cutVideo(new FileInputStream(“C:\Users\Xpdn\Videos\3.mp4”),
new File(“C:\Users\Xpdn\Videos\output.mp4”), 30L, 900L);
// t2();
}
public static File cutVideo(InputStream inputStream, File outFile, long beginSecond, long endSecond) throws Exception {
FFmpegFrameGrabber grabber = null;
FFmpegFrameRecorder recorder = null;
try {
grabber = new FFmpegFrameGrabber(inputStream);
grabber.start();
if (beginSecond < 0 || (grabber.getLengthInTime() / 1000000L) <= endSecond) {
return null;
}
long numFrame = (long) ((endSecond - beginSecond) * grabber.getFrameRate()) * 2; // 120 * 25
// grabber.setVideoTimestamp(beginSecond * 1000000L);
grabber.setVideoFrameNumber((int) (grabber.getFrameRate() * beginSecond)); // 第分钟开始
recorder = new FFmpegFrameRecorder(outFile, grabber.getImageWidth(), grabber.getImageHeight());
recorder.setFormat("mp4");
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
recorder.setVideoBitrate(grabber.getVideoBitrate());
recorder.setFrameRate(grabber.getFrameRate());
recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
recorder.setAudioChannels(2);
recorder.start();
Frame frame;
long num = 0;
while ((frame = grabber.grabFrame()) != null && (numFrame >= num)) {
num++;
// 处理每一帧的图像或音频数据
recorder.record(frame);
}
return outFile;
}catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (grabber != null) {
grabber.stop();
grabber.release();
}
if (recorder != null) {
recorder.stop();
recorder.release();
}
} catch (FFmpegFrameGrabber.Exception e1) {
e1.printStackTrace();
}
}
return null;
}
我的完整的工具类
package com.ruoyi.file.utils;
import cn.hutool.core.io.FileUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.util.MapUtil;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.*;
import org.opencv.video.Video;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.;
import java.security.SecureRandom;
import java.util.;
@Slf4j
public class VideoUtils {
public static void main(String[] args) throws Exception {
// t1();
cutVideo(new FileInputStream(“C:\Users\Xpdn\Videos\3.mp4”),
new File(“C:\Users\Xpdn\Videos\output.mp4”), 30L, 900L);
// t2();
}
public static File cutVideo(InputStream inputStream, File outFile, long beginSecond, long endSecond) throws Exception {
FFmpegFrameGrabber grabber = null;
FFmpegFrameRecorder recorder = null;
try {
grabber = new FFmpegFrameGrabber(inputStream);
grabber.start();
if (beginSecond < 0 || (grabber.getLengthInTime() / 1000000L) <= endSecond) {
return null;
}
long numFrame = (long) ((endSecond - beginSecond) * grabber.getFrameRate()) * 2; // 120 * 25
// grabber.setVideoTimestamp(beginSecond * 1000000L);
grabber.setVideoFrameNumber((int) (grabber.getFrameRate() * beginSecond)); // 第1分钟开始
recorder = new FFmpegFrameRecorder(outFile, grabber.getImageWidth(), grabber.getImageHeight());
recorder.setFormat("mp4");
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
recorder.setVideoBitrate(grabber.getVideoBitrate());
recorder.setFrameRate(grabber.getFrameRate());
recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
recorder.setAudioChannels(2);
recorder.start();
Frame frame;
long num = 0;
while ((frame = grabber.grabFrame()) != null && (numFrame >= num)) {
num++;
// 处理每一帧的图像或音频数据
recorder.record(frame);
}
return outFile;
}catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (grabber != null) {
grabber.stop();
grabber.release();
}
if (recorder != null) {
recorder.stop();
recorder.release();
}
} catch (FFmpegFrameGrabber.Exception e1) {
e1.printStackTrace();
}
}
return null;
}
public static void t2() throws Exception {
// OutputStream outputStream = new FileOutputStream(“C:\Users\Xpdn\Videos\1xxx.mp4”);
OutputStream outputStream = new ByteArrayOutputStream(1024102410);
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber("C:\\Users\\Xpdn\\Videos\\1.mp4");
grabber.start();
long beginSecond = 60;
long endSecond = 120;
long numFrame = (long) ((endSecond - beginSecond) * grabber.getFrameRate());// 60 * 25
grabber.setVideoTimestamp(beginSecond * 1000000L);
//grabber.setVideoFrameNumber((int) (25 * beginSecond)); // 2分钟开始
File file = new File("C:\\Users\\Xpdn\\Videos\\1xxx.mp4");
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputStream, grabber.getImageWidth(),
grabber.getImageHeight(), grabber.getAudioChannels());
recorder.setFormat("mp4");
// recorder.setOption();
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
recorder.setVideoBitrate(grabber.getVideoBitrate());
recorder.setFrameRate(grabber.getFrameRate());
recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
recorder.start();
Frame frame;
long num = 0;
while ((frame = grabber.grabFrame()) != null && (numFrame >= num)) {
num++;
// 处理每一帧的图像或音频数据
recorder.record(frame);
}
recorder.stop();
recorder.release();
}
public static void t1() throws Exception {
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber("C:\\Users\\Xpdn\\Videos\\VCG42N1340724607.mp4");
avutil.av_log_set_level(avutil.AV_LOG_ERROR);
FFmpegLogCallback.set();
grabber.start();
int lengthInVideoFrames = grabber.getLengthInVideoFrames();
long lengthInTime = grabber.getLengthInTime();
double videoFrameRate = grabber.getVideoFrameRate();
grabber.setVideoTimestamp(1 * 1000000L);
grabber.setVideoFrameNumber(25 * 120);
FileOutputStream outputStream = new FileOutputStream("C:\\\\Users\\\\Xpdn\\\\Videos\\\\VCG42N1340724607xx.mp4");
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputStream, grabber.getImageWidth(),
grabber.getImageHeight(),grabber.getAudioChannels());
recorder.setFormat("mp4");
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
// recorder.setVideoCodec(grabber.getVideoCodec());
recorder.setAudioCodec(grabber.getAudioCodec());
recorder.setVideoBitrate(grabber.getVideoBitrate());
recorder.setFrameRate(grabber.getFrameRate());
recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
recorder.start();
Frame frame;
while ((frame = grabber.grabFrame()) != null) {
// 处理每一帧的图像或音频数据
recorder.record(frame);
}
// recorder.stop();
// recorder.release();
}
public static Map<String, InputStream> clipVideo(InputStream inputStream, Map<Long,Long> timePeriod) throws Exception {
boolean isStart = true;// 该变量建议设置为全局控制变量,用于控制录制结束
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputStream);
grabber.start();
//好多视频熟悉可以获取后打印 示例几个
log.info("ImageWidth:" + grabber.getImageWidth());
log.info("ImageHeight:" + grabber.getImageHeight());
log.info("AudioChannels:" + grabber.getAudioChannels());
log.info("Format:" + grabber.getFormat());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputStream, grabber.getAudioChannels());
recorder.setFrameRate(grabber.getFrameRate());
// recorder.setAudioBitrate(grabber.getAudioBitrate());
// recorder.setSampleRate(grabber.getSampleRate());
// recorder.setGopSize(2);
recorder.setFormat(grabber.getFormat());
recorder.setAudioCodecName(“aac”);
recorder.setVideoCodec(grabber.getVideoCodec());
Frame f = null;
//如果想截取规定时间段视频 请看系列文章二
while (isStart) {
f = grabber.grabFrame();
recorder.record(f);
}
recorder.stop();
recorder.release();
grabber.stop();
grabber.release();
return null;
}
/**
* 将视频文件inputStream流截屏
*
* @param inputStream
* @param seconds 截屏的数量以及秒数
* @return
*/
public static Map<String, InputStream> generateCover(InputStream inputStream, List<Long> seconds) {
// 生成封面
HashMap<String, InputStream> hashMap = new HashMap<>();
FFmpegFrameGrabber grabber = null;
try {
// 初始化FFmpegFrameGrabber
grabber = new FFmpegFrameGrabber(inputStream);
grabber.start();
// 获取截取秒数,循环生成封面
for (Long second : seconds) {
// 设置封面截取时间
grabber.setTimestamp(second * 1000000L);
Frame f = grabber.grabImage();
Java2DFrameConverter converter = new Java2DFrameConverter();
BufferedImage bi = converter.getBufferedImage(f);
// 指定封面保存地址
ByteArrayOutputStream os = new ByteArrayOutputStream();
// 生成封面图片
ImageIO.write(bi, "jpg", os);
InputStream input = new ByteArrayInputStream(os.toByteArray());
hashMap.put(String.valueOf(second), input);
}
return hashMap;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (grabber != null) {
grabber.stop();
grabber.release();
}
} catch (FFmpegFrameGrabber.Exception e) {
e.printStackTrace();
}
}
return null;
}
/**
* 将视频文件inputStream流截屏,可以指定随机或者平均
* @param inputStream
* @param num
* @param isRandom
* @return
*/
public static Map<String, InputStream> generateCover(InputStream inputStream, Long num, boolean isRandom) {
// 生成封面
HashMap<String, InputStream> hashMap = new HashMap<>();
FFmpegFrameGrabber grabber = null;
try {
// 初始化FFmpegFrameGrabber
grabber = new FFmpegFrameGrabber(inputStream);
grabber.start();
Set<Long> seconds = null;
if (isRandom) {
seconds = generateSecondList(grabber, num, true);
} else {
seconds = averageSecondList(grabber, num);
}
// 获取截取秒数,循环生成封面
for (Long second : seconds) {
// 设置封面截取时间
grabber.setTimestamp(second * 1000000L);
Frame f = grabber.grabImage();
Java2DFrameConverter converter = new Java2DFrameConverter();
BufferedImage bi = converter.getBufferedImage(f);
// 指定封面保存地址
ByteArrayOutputStream os = new ByteArrayOutputStream();
// 生成封面图片
ImageIO.write(bi, "jpg", os);
InputStream input = new ByteArrayInputStream(os.toByteArray());
hashMap.put(String.valueOf(second), input);
}
return hashMap;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (grabber != null) {
grabber.stop();
grabber.release();
}
} catch (FFmpegFrameGrabber.Exception e) {
e.printStackTrace();
}
}
return null;
}
/**
* 生成视频封面
*
* @param videoFile 视频全路径,目前发现支持mp4、webm,其他的需要自己去尝试
* @param coverPath
* @param coverNum 封面数量
* @param random 是否随机生成封面
* @return map,其中key是秒数,而value是图片全路径
**/
public static Map<String, String> generateCover(File videoFile, String coverPath, long coverNum, boolean random, Set<Long> seconds) {
// 验证视频
if (!(videoFile != null && videoFile.isFile() && videoFile.exists())) {
throw new RuntimeException("参数:videoFile 不符合要求");
}
// 验证封面数量
if (coverNum <= 0) {
throw new RuntimeException("参数:coverNum 必须大于0");
}
// 存储封面
Map<String, String> coverMap = new TreeMap<>();
// 生成封面
FFmpegFrameGrabber grabber = null;
try {
// 初始化FFmpegFrameGrabber
grabber = new FFmpegFrameGrabber(videoFile);
grabber.start();
// 获取截取秒数
Set<Long> secondSet = seconds == null ? generateSecondList(grabber, coverNum, random) : seconds;
// 循环生成封面
for (Long second : secondSet) {
// 设置封面截取时间
grabber.setTimestamp(second * 1000000L);
Frame f = grabber.grabImage();
Java2DFrameConverter converter = new Java2DFrameConverter();
BufferedImage bi = converter.getBufferedImage(f);
// 指定封面保存地址
// File imgFile = createTempFile(“cover.jpg”);
String videoFileName = videoFile.getName();
File imgFile = new File(coverPath, videoFileName.substring(0, videoFileName.lastIndexOf(“.”)) + “.jpg”);
// 生成封面图片
ImageIO.write(bi, “jpg”, imgFile);
String s = toBase64Img(imgFile);
coverMap.put(“imgBase64”, s);
imgFile.delete();
// 装载封面图片
// coverMap.put(second, imgFile.getAbsolutePath());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (grabber != null) {
grabber.stop();
grabber.release();
}
} catch (FFmpegFrameGrabber.Exception e) {
e.printStackTrace();
}
}
return coverMap;
}
/**
* 图片转换成base64格式
*
* @param file
* @return
*/
public static String toBase64Img(File file) {
FileInputStream fileInputStream = null;
String s = null;
try {
fileInputStream = new FileInputStream(file);
byte[] encode = Base64.getEncoder().encode(fileInputStream.readAllBytes());
s = new String(encode);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return "data:image/jpg;base64," + s;
}
public static Long getVideoTotalSecond(InputStream inputStream) {
// 生成封面
FFmpegFrameGrabber grabber = null;
try {
// 初始化FFmpegFrameGrabber
grabber = new FFmpegFrameGrabber(inputStream);
grabber.start();
return grabber.getLengthInTime() / 1000000;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (grabber != null) {
grabber.stop();
grabber.release();
}
} catch (FFmpegFrameGrabber.Exception e) {
e.printStackTrace();
}
}
return -1L;
}
/**
* 随机获取封面截图秒数集合
*
* @param grabber FFmpegFrameGrabber对象
* @param coverNum 封面数量
* @param random 是否随机生成封面
* @return 封面截图秒数集合
* @author 明快de玄米61
* @date 2022/12/4 17:51
**/
@SneakyThrows
private static Set<Long> generateSecondList(FFmpegFrameGrabber grabber, long coverNum, boolean random) {
// 存储封面截图秒数集合
Set<Long> secondSet = new TreeSet<>();
// 获取视频时长秒数;说明:除以1000000可以获取秒数
long totalVideoSecond = grabber.getLengthInTime() / 1000000;
// 随机生成截图时间
if (random) {
Random secureRandom = SecureRandom.getInstanceStrong();
for (int i = 1; i <= coverNum; i++) {
secondSet.add((long) (secureRandom.nextInt((int) totalVideoSecond - 1) + 1));
}
} else {
// 自定义截图时间
coverNum = totalVideoSecond > coverNum ? coverNum : totalVideoSecond;
for (long i = 1; i <= coverNum; i++) {
secondSet.add(i);
}
}
return secondSet;
}
/**
* 平均获取视频的秒数集合
*
* @param grabber
* @param coverNum
* @return
*/
private static Set<Long> averageSecondList(FFmpegFrameGrabber grabber, long coverNum) {
// 存储封面截图秒数集合
Set<Long> secondSet = new TreeSet<>();
// 获取视频时长秒数;说明:除以1000000可以获取秒数
long totalVideoSecond = grabber.getLengthInTime() / 1000000;
long l = totalVideoSecond / (coverNum + 1); // 平均的秒数间隔
for (long i = 0; i < coverNum; i++) {
secondSet.add((i + 1) * l);
}
return secondSet;
}
}