赞
踩
我们在很多项目中实现过上传视频,为了展示视频,我们经常会有获取视频第一帧作为视频封面的操作,以下就是获取视频第一帧的代码实现。
一般情况下,作为服务端的开发者,一个需求有两种大概的实现方向:
(1、自己实现
(2、甩锅给前端
1、依赖
- <!-- 获取视频第一帧依赖 -->
- <dependency>
- <groupId>org.bytedeco</groupId>
- <artifactId>javacpp</artifactId>
- <version>1.4.1</version>
- </dependency>
- <dependency>
- <groupId>org.bytedeco</groupId>
- <artifactId>javacv</artifactId>
- <version>1.4.1</version>
- </dependency>
- <dependency>
- <groupId>org.bytedeco.javacpp-presets</groupId>
- <artifactId>opencv-platform</artifactId>
- <version>3.4.1-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>
2、实际代码实现(截取第一帧,详情留意代码注释)
- /**
- *
- * @ClassName: FileUtil
- * @Description: 文件管理
- * @author: Zing
- * @date: 2019年12月11日 上午11:55:40
- * @Copyright:
- */
- public class FileUtil {
- /**
- *
- * @Title: getTempPath
- * @Description: 生成视频的首帧图片方法
- * @author: Zing
- * @param: @param tempPath 生成首帧图片的文件地址
- * @param: @param filePath 传进来的线上文件
- * @param: @return
- * @param: @throws Exception
- * @return: boolean
- * @throws
- */
- public static boolean getTempPath(String tempPath, String filePath) throws Exception {
- File targetFile = new File(tempPath);
- if (!targetFile.getParentFile().exists()) {
- targetFile.getParentFile().mkdirs();
- }
- File file2 = new File(filePath);
- //判断文件是否为视频
- if(FileUtil.isVideo("filePath")) {
- if (file2.exists()) {
- FFmpegFrameGrabber ff = new FFmpegFrameGrabber(file2);
- ff.start();
- int ftp = ff.getLengthInFrames();
- int flag=0;
- Frame frame = null;
- while (flag <= ftp) {
- //获取帧
- frame = ff.grabImage();
- //过滤前3帧,避免出现全黑图片
- if ((flag>3)&&(frame != null)) {
- break;
- }
- flag++;
- }
- if(ImageIO.write(FrameToBufferedImage(frame), "jpg", targetFile)) {
- ff.close();
- ff.stop();
- return true;
- }else {
- ff.close();
- ff.stop();
- return false;
- }
-
- }
- }
- return false;
- }
-
- /***
- *
- * @Title: FrameToBufferedImage
- * @Description: 创建格式化BufferedImage对象
- * @author: Zing
- * @param: @param frame
- * @param: @return
- * @return: RenderedImage
- * @throws
- */
- private static RenderedImage FrameToBufferedImage(Frame frame) {
- //创建BufferedImage对象
- Java2DFrameConverter converter = new Java2DFrameConverter();
- BufferedImage bufferedImage = converter.getBufferedImage(frame);
- return bufferedImage;
- }
-
- /**
- *
- * @Title: isVideo
- * @Description:判断是不是视频
- * @author: Zing
- * @param: @param path 文件路径
- * @param: @return
- * @return: boolean true是视频 false非视频
- * @throws
- */
- public static boolean isVideo(String path) {
- List<String> typeList = new ArrayList<String>();
- typeList.add("mp4");
- typeList.add("flv");
- typeList.add("avi");
- typeList.add("rmvb");
- typeList.add("rm");
- typeList.add("wmv");
- //获取文件名和后缀
- String suffix = path.substring(path.lastIndexOf(".") + 1);
- for(String type : typeList) {
- if(type.toUpperCase().equals(suffix.toUpperCase())) {
- return true;
- }
- }
- return false;
- }
-
- /**
- *
- * @Title: isVideo
- * @Description: 判断是不是图片
- * @author: Zing
- * @param: @param path 文件路径
- * @param: @return
- * @return: boolean true是图片 false非图片
- * @throws
- */
- public static boolean isImage(String path) {
- List<String> typeList = new ArrayList<String>();
- typeList.add("jpg");
- typeList.add("png");
- //获取文件名和后缀
- String suffix = path.substring(path.lastIndexOf(".") + 1);
- for(String type : typeList) {
- if(type.toUpperCase().equals(suffix.toUpperCase())) {
- return true;
- }
- }
- return false;
- }
- }
应用场景和架构设计建议:这一部分其实完全可以抽离出来做异步或者MQ消费,把异步执行的结果回传到我们视频结果里面。
例如在MQ场景下,我们可以将视频上传和处理进行结束后发送MQ消息,MQ消费者在接收到对应的MQ消息后,对库表中存在的文件资源地址进行解析参考我的另一篇文章:
网络地址 URL 文件转MultipartFilehttps://blog.csdn.net/weixin_41719650/article/details/119914300?spm=1001.2014.3001.5501然后再通过该方法进行流处理,获取第一张图片后回写到数据库进行保存。这样可以保证主业务解耦,还有主服务器流对象的内存控制。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。