Bytedeco
通过视频链接进行关键帧抽取图片,利用FFmpegFrameGrabber对视频流进行抽帧处理。
一、引入POM依赖
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>ffmpeg-platform</artifactId>
<version>3.4.2-1.4.1</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>1.5.8</version>
</dependency>
二、抽帧代码实现
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
/**
* 视频抽帧工具
*/
@Slf4j
public class VideoFrame {
public static Map<Integer, InputStream> process(String videoUrl, Integer stepSecond, Integer count, String uuid, String ipProxy) {
Map<Integer, InputStream> result = null;
int num = 0;
while (num < 2) {
log.info("开始抽帧");
result = videoUrlIntercept(videoUrl, stepSecond, count, uuid, ipProxy);
if (result.size() > 0) {
log.info("uuid:{},第{}次抽帧成功", uuid, num + 1);
break;
} else {
//第一次使用ipProxy不成功就更换
log.info("uuid:{},第{}次抽帧保存失败,ipProxy:{}", uuid, num + 1, ipProxy);
ipProxy = null;
num++;
}
}
return result;
}
/**
* 视频文件边下载边抽帧1秒1帧
*
* @param videoUrl 网络视频文件URL
* @param stepSecond 每隔几秒取一帧,默认1s
* @param count 需要截取的帧个数
* @param uuid uuid
* @return
*/
public static Map<Integer, InputStream> videoUrlIntercept(String videoUrl, Integer stepSecond, Integer count, String uuid, String ipProxy) {
Map<Integer, InputStream> files = new HashMap<>();
stepSecond = stepSecond == null ? 1 : stepSecond;
FFmpegFrameGrabber ff = new FFmpegFrameGrabber(videoUrl);
// 设置超时时间为40秒
ff.setOption("timeout", "40000000");
ff.setOption("user_agent", UserAgent.getUserAgent());
try {
ff.start();
long timeLength = ff.getLengthInTime();
Frame frame = ff.grabImage();
long startTime = frame.timestamp;
long timestamp = 0;
int second = 0;
int picNum = 0;
while (timestamp <= timeLength) {
log.info("uuid:{},抽取第{}帧,video_url:{}",uuid,picNum,videoUrl);
timestamp = startTime + second * 1000000L;
ff.setTimestamp(timestamp);
frame = ff.grabImage();
if (frame != null) {
if (frame.image != null) {
InputStream inputStream = doExecuteFrame(frame, picNum);
if (inputStream != null) {
files.put(picNum, inputStream);
}
picNum++;
if (count != null && picNum == count) {
break;
}
}
}
second += stepSecond;
if(picNum > 60) {
break;
}
}
ff.stop();
} catch (Exception e) {
log.error("下载抽帧失败,uuid:{},ipPort:{},videoUrl:{},msg:{}", uuid, null, videoUrl, e.getMessage());
e.printStackTrace();
}
return files;
}
/**
* 视频文件指定时间段的帧截取
*
* @param videoUrl 视频文件URL
* @param start 视频开始的帧
* @param count 需要截取的帧个数
* @param isAvgTime 在截帧时 是否均匀分布计算时间
* @return
*/
public static Map<Integer, InputStream> videoIntercept(String videoUrl, int start, int count, boolean isAvgTime) {
log.info("开始抽取视频帧数,videoUrl:{}",videoUrl);
Frame frame = null;
//<时间, 图片流>
Map<Integer, InputStream> files = new HashMap<>();
FFmpegFrameGrabber fFmpegFrameGrabber = new FFmpegFrameGrabber(videoUrl);
fFmpegFrameGrabber.setOption("timeout", "40000000");
try {
fFmpegFrameGrabber.start();
long frameTime = 1;
if (isAvgTime) {
frameTime = fFmpegFrameGrabber.getLengthInTime() / count / 1000000L;
if (frameTime < 0) {
frameTime = 1;
}
}
for (int i = start; i <= count; i++) {
fFmpegFrameGrabber.setTimestamp(i * frameTime * 1000 * 1000);
frame = fFmpegFrameGrabber.grabImage();
InputStream inputStream = doExecuteFrame(frame, i);
if (inputStream != null) {
files.put(i, inputStream);
}
}
fFmpegFrameGrabber.stop();
} catch (Exception E) {
log.info("下载的视频抽帧失败,msg:" + E.getMessage());
E.printStackTrace();
}
return files;
}
public static InputStream doExecuteFrame(Frame frame, int index) {
if (frame == null || frame.image == null) {
return null;
}
Java2DFrameConverter converter = new Java2DFrameConverter();
BufferedImage bi = converter.getBufferedImage(frame);
InputStream inputStream = bufferedImageToInputStream(bi);
return inputStream;
}
public static InputStream bufferedImageToInputStream(BufferedImage image) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
ImageIO.write(image, "jpg", os);
InputStream input = new ByteArrayInputStream(os.toByteArray());
return input;
} catch (IOException e) {
}
return null;
}
public static void main(String[] args) throws IOException {
String videoUrl = "http://vd2.bdstatic.com/mda-pej1ztfufz8axvtu/360p/h264/1684545921389774683/mda-pej1ztfufz8axvtu.mp4";
Map<Integer, InputStream> integerInputStreamMap = videoIntercept(videoUrl, 1, 13, false);
System.out.println(integerInputStreamMap.size());
for (Integer seconds : integerInputStreamMap.keySet()) {
InputStream inputStream = integerInputStreamMap.get(seconds);
String fileName = MD5Util.createMd5(System.currentTimeMillis()+"抖音测试3" + "_" + seconds) + ".jpg";
String filePath = "/test/01/"+fileName;
//本地磁盘存储
String uploadURL = PictureDownload.downloadFrame(fileName, "D:/image" + filePath, inputStream, "douyin", true);
System.out.println("seconds: " + seconds + ", uploadURL: " + uploadURL);
}
}
}