前言: 我们在很多时候需要对视频文件进行分析,或者对视频产生缩略图。因此视频截取技术必不可少。

从本地文件中读取视频帧

导包

org.jcodec

jcodec

0.2.5

org.jcodec

jcodec-javase

0.2.5

commons-io

commons-io

2.6

读取视频帧工具类

package com.wkl.testdemo.vedio;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.io.FileUtils;

import org.jcodec.api.FrameGrab;

import org.jcodec.api.JCodecException;

import org.jcodec.common.model.Picture;

import org.jcodec.scale.AWTUtil;

import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;

import java.awt.image.BufferedImage;

import java.io.*;

import java.net.URL;

import java.util.UUID;

/**

* 视频操作工具类

*/

@Slf4j

public class VideoUtils {

/*** 图片格式*/

private static final String FILE_EXT = "jpg";

/*** 帧数-第几帧*/

private static final int THUMB_FRAME = 500;

/**

* 获取指定视频的帧并保存为图片至指定目录

*

* @param videoFilePath 源视频文件路径

* @param frameFilePath 截取帧的图片存放路径

*/

public static void fetchFrame(String videoFilePath, String frameFilePath) throws Exception {

File videoFile = new File(videoFilePath);

File frameFile = new File(frameFilePath);

getThumbnail(videoFile, frameFile);

}

/**

* 获取指定视频的帧并保存为图片至指定目录

*

* @param videoFile 源视频文件

* @param targetFile 截取帧的图片

*/

public static void fetchFrame(MultipartFile videoFile, File targetFile) throws Exception {

File file = new File(videoFile.getName());

FileUtils.copyInputStreamToFile(videoFile.getInputStream(), file);

getThumbnail(file, targetFile);

}

/**

* 获取指定视频的帧并保存为图片至指定目录

*

* @param videoFile 源视频文件

*/

public static File fetchFrame(MultipartFile videoFile) {

String originalFilename = videoFile.getOriginalFilename();

File file = new File(originalFilename);

File targetFile = null;

try {

FileUtils.copyInputStreamToFile(videoFile.getInputStream(), file);

int i = originalFilename.lastIndexOf(".");

String imageName;

if (i > 0) {

imageName = originalFilename.substring(0, i);

} else {

imageName = UUID.randomUUID().toString().replace("-", "");

}

imageName = imageName + ".jpg";

targetFile = new File(imageName);

getThumbnail(file, targetFile);

} catch (Exception e) {

log.error("获取视频指定帧异常:", e);

} finally {

if (file.exists()) {

file.delete();

}

}

log.debug("视频文件 - 帧截取 - 处理结束");

return targetFile;

}

/**

* 获取第一帧缩略图

*

* @param videoFile 视频路径

* @param targetFile 缩略图目标路径

*/

public static void getThumbnail(File videoFile, File targetFile) {

try {

// 根据扩展名创建一个新文件路径

Picture picture = FrameGrab.getFrameFromFile(videoFile, THUMB_FRAME);

BufferedImage bufferedImage = AWTUtil.toBufferedImage(picture);

ImageIO.write(bufferedImage, FILE_EXT, targetFile);

} catch (IOException | JCodecException e) {

e.printStackTrace();

log.error("获取第一帧缩略图异常:", e);

}

}

public static void main(String[] args) {

try {

long startTime = System.currentTimeMillis();

getThumbnail(new File("D:\\Videos\\2023112911533869304715.mp4"), new File("D:\\Videos\\test1.jpg"));

System.out.println("截取图片耗时:" + (System.currentTimeMillis() - startTime));

} catch (Exception e) {

e.printStackTrace();

}

}

public static void saveImage(String imageUrl, String destinationFile) throws IOException {

URL url = new URL(imageUrl);

InputStream is = url.openStream();

OutputStream os = new FileOutputStream(destinationFile);

byte[] b = new byte[2048];

int length;

while ((length = is.read(b)) != -1) {

os.write(b, 0, length);

}

is.close();

os.close();

}

}

从网络url 读取视频帧-ffmpeg

导包

org.bytedeco

javacpp

1.4.1

org.bytedeco

javacv

1.4.1

org.bytedeco.javacpp-presets

ffmpeg-platform

3.4.2-1.4.1

工具类

package com.wkl.testdemo.vedio;

import lombok.extern.slf4j.Slf4j;

import org.bytedeco.javacv.FFmpegFrameGrabber;

import org.bytedeco.javacv.Frame;

import org.bytedeco.javacv.FrameGrabber;

import org.bytedeco.javacv.Java2DFrameConverter;

import javax.imageio.ImageIO;

import java.awt.image.BufferedImage;

import java.io.*;

import java.util.HashMap;

import java.util.Map;

/**

* 视频抽帧工具

*/

@Slf4j

public class VideoFrame {

//传入包含特定时间的Frame ,读取图片

public static BufferedImage doExecuteFrame(Frame frame, int index) {

if (frame == null || frame.image == null) {

return null;

}

Java2DFrameConverter converter = new Java2DFrameConverter();

BufferedImage bi = converter.getBufferedImage(frame);

return bi;

}

/*

* @description: 读取视频第n秒的图片帧

* @author: wangkanglu

* @date: 2023/12/8 15:05

* @param: [videoUrl, stepSecond]

* @return: java.awt.image.BufferedImage

**/

public static BufferedImage doExecuteFrameByTime(String videoUrl, Integer stepSecond) {

FFmpegFrameGrabber ff = new FFmpegFrameGrabber(videoUrl);

ff.setOption("timeout", "40000000");

long timestamp = stepSecond * 1000000L; //视频是按照微秒计算的,10的6次方分之一秒

try {

ff.start();

ff.setTimestamp(timestamp);

Frame frame = ff.grabImage();

if (frame == null || frame.image == null) {

return null;

}

Java2DFrameConverter converter = new Java2DFrameConverter();

BufferedImage bi = converter.getBufferedImage(frame);

ff.stop();

return bi;

} catch (FrameGrabber.Exception e) {

throw new RuntimeException(e);

}

}

/**

* 视频文件边下载边抽帧1秒1帧

*

* @param videoUrl 网络视频文件URL

* @param stepSecond 每隔几秒取一帧,默认1s

* @param count 需要截取的帧个数

* @return

*/

public static Map videoUrlIntercept(String videoUrl, Integer stepSecond, Integer count) {

Map 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;//第n张帧

while (timestamp <= timeLength) {

log.info("抽取第{}帧,video_url:{}",picNum,videoUrl);

timestamp = startTime + second * 1000000L; //视频是按照微秒计算的,10的6次方分之一秒

ff.setTimestamp(timestamp);

frame = ff.grabImage();

if (frame != null) {

if (frame.image != null) {

BufferedImage bufferedImage = doExecuteFrame(frame, picNum);

if (bufferedImage != null) {

files.put(picNum, bufferedImage);

}

picNum++;

if (count != null && picNum == count) {

break;

}

}

}

second += stepSecond;

if(picNum > 60) {

break;

}

}

ff.stop();

} catch (Exception e) {

log.error("下载抽帧失败,ipPort:{},videoUrl:{},msg:{}", null, videoUrl, e.getMessage());

e.printStackTrace();

}

return files;

}

/**

* 视频文件指定时间段的帧截取

*

* @param videoUrl 视频文件URL

* @param start 视频开始的帧

* @param count 需要截取的帧个数

* @param isAvgTime 在截帧时 是否均匀分布计算时间

* @return

*/

public static Map videoIntercept(String videoUrl, int start, int count, boolean isAvgTime) {

log.info("开始抽取视频帧数,videoUrl:{}",videoUrl);

Frame frame = null;

//<时间, 图片流>

Map 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();

BufferedImage bufferedImage = doExecuteFrame(frame, i);

if (bufferedImage != null) {

files.put(i, bufferedImage);

}

}

fFmpegFrameGrabber.stop();

} catch (Exception E) {

log.info("下载的视频抽帧失败,msg:" + E.getMessage());

E.printStackTrace();

}

return files;

}

/*

* @description: BufferedImage 转 inputStream

* @author: wangkanglu

* @date: 2023/12/8 14:52

* @param: [image]

* @return: java.io.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 integerInputStreamMap = videoUrlIntercept(videoUrl, 1, 13);

System.out.println(integerInputStreamMap.size());

for (Integer seconds : integerInputStreamMap.keySet()) {

BufferedImage bufferedImage = integerInputStreamMap.get(seconds);

String fileName = System.currentTimeMillis()+"抖音测试3" + "_" + seconds + ".jpg";

String filePath = "D:\\Videos\\"+fileName;

//本地磁盘存储

// 本地图片保存地址

ImageIO.write(bufferedImage, "png", new File(filePath));

System.out.println("seconds: " + seconds + ", uploadURL: " + filePath);

}

}

}

附赠压缩图片工具类

package com.wkl.testdemo.vedio;

import lombok.extern.slf4j.Slf4j;

import org.apache.tomcat.util.codec.binary.Base64;

import org.bytedeco.javacv.FFmpegFrameGrabber;

import org.bytedeco.javacv.Frame;

import org.bytedeco.javacv.FrameGrabber;

import org.bytedeco.javacv.Java2DFrameConverter;

import org.springframework.stereotype.Component;

import javax.imageio.ImageIO;

import java.awt.*;

import java.awt.image.BufferedImage;

import java.io.ByteArrayInputStream;

import java.io.ByteArrayOutputStream;

import java.io.File;

import java.io.IOException;

@Slf4j

@Component

public class VideoFrameGrabber {

public static void main(String[] args) throws Exception {

// 视频地址

String vedioUrl = "http://vd2.bdstatic.com/mda-pej1ztfufz8axvtu/360p/h264/1684545921389774683/mda-pej1ztfufz8axvtu.mp4";

BufferedImage image = doExecuteFrameByTime(vedioUrl, 10);

double targetSize = 10*1024;

while (imageToBytes(image).length > targetSize) {

float reduceMultiple = 0.5f;

image = resizeImage(image, reduceMultiple);

}

// 本地图片保存地址

ImageIO.write(image, "png", new File("D:\\Videos\\test6.jpg"));

}

/*

* @description: 读取视频第n秒的图片帧

* @author: wangkanglu

* @date: 2023/12/8 15:05

* @param: [videoUrl, stepSecond]

* @return: java.awt.image.BufferedImage

**/

public static BufferedImage doExecuteFrameByTime(String videoUrl, Integer stepSecond) {

FFmpegFrameGrabber ff = new FFmpegFrameGrabber(videoUrl);

ff.setOption("timeout", "40000000");

long timestamp = stepSecond * 1000000L; //视频是按照微秒计算的,10的6次方分之一秒

try {

ff.start();

ff.setTimestamp(timestamp);

Frame frame = ff.grabImage();

if (frame == null || frame.image == null) {

return null;

}

Java2DFrameConverter converter = new Java2DFrameConverter();

BufferedImage bi = converter.getBufferedImage(frame);

ff.stop();

return bi;

} catch (FrameGrabber.Exception e) {

throw new RuntimeException(e);

}

}

/**

* 通过BufferedImage图片流调整图片大小

* 指定压缩后长宽

*/

public static BufferedImage resizeImage(BufferedImage originalImage, int targetWidth, int targetHeight) throws IOException {

Image resultingImage = originalImage.getScaledInstance(targetWidth, targetHeight, Image.SCALE_AREA_AVERAGING);

BufferedImage outputImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);

outputImage.getGraphics().drawImage(resultingImage, 0, 0, null);

return outputImage;

}

/**

* 通过BufferedImage图片流调整图片大小

* @param originalImage

* @param reduceMultiple 缩小倍数

* @return

* @throws IOException

*/

public static BufferedImage resizeImage(BufferedImage originalImage, float reduceMultiple) throws IOException {

int width = (int) (originalImage.getWidth() * reduceMultiple);

int height = (int) (originalImage.getHeight() * reduceMultiple);

Image resultingImage = originalImage.getScaledInstance(width, height, Image.SCALE_AREA_AVERAGING);

BufferedImage outputImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

outputImage.getGraphics().drawImage(resultingImage, 0, 0, null);

return outputImage;

}

/**

* 压缩图片到指定大小

* @param srcImgData

* @param reduceMultiple 每次压缩比率

* @return

* @throws IOException

*/

public static byte[] resizeImage(byte[] srcImgData, float reduceMultiple) throws IOException {

BufferedImage bi = ImageIO.read(new ByteArrayInputStream(srcImgData));

int width = (int) (bi.getWidth() * reduceMultiple); // 源图宽度

int height = (int) (bi.getHeight() * reduceMultiple); // 源图高度

Image image = bi.getScaledInstance(width, height, Image.SCALE_SMOOTH);

BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

Graphics g = tag.getGraphics();

g.setColor(Color.RED);

g.drawImage(image, 0, 0, null); // 绘制处理后的图

g.dispose();

ByteArrayOutputStream bOut = new ByteArrayOutputStream();

ImageIO.write(tag, "JPEG", bOut);

return bOut.toByteArray();

}

/**

* BufferedImage图片流转byte[]数组

*/

public static byte[] imageToBytes(BufferedImage bImage) {

ByteArrayOutputStream out = new ByteArrayOutputStream();

try {

ImageIO.write(bImage, "jpg", out);

} catch (IOException e) {

e.printStackTrace();

}

return out.toByteArray();

}

/**

* byte[]数组转BufferedImage图片流

*/

private static BufferedImage bytesToBufferedImage(byte[] ImageByte) {

ByteArrayInputStream in = new ByteArrayInputStream(ImageByte);

BufferedImage image = null;

try {

image = ImageIO.read(in);

} catch (IOException e) {

e.printStackTrace();

}

return image;

}

}

推荐文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: