package com.yanzuoguang.util; import com.yanzuoguang.util.log.Log; import org.bytedeco.ffmpeg.avcodec.AVPacket; import org.bytedeco.javacv.*; import org.bytedeco.opencv.global.opencv_core; import org.bytedeco.opencv.opencv_core.IplImage; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * 视频帮助类 * <p> * 59s视频,抖音上拍摄59s,下载后是27.79mb。先从手机上拍摄59s,原视频是111mb,上传抖音后下载的视频是47.32mb * * @author 颜佐光 */ public class MediaHelper extends ImageHelper { /** * 默认截取视频的中间帧为封面 */ public static final int FRAME_INDEX = 3; public static boolean Frame = true; /** * 从视频文件中获取第一张图片 * * @param fromFile 视频文件 * @param toFile 转码后保存的文件 * @param parameter 转码后的参数,在转码完成后该参数值会被改变 */ public static void zipVideoMp4(String fromFile, String toFile, MediaParameter parameter) throws IOException { // 视频压缩参数初始化 if (parameter == null) { parameter = new MediaParameter(); } parameter.initZipMp4(); convertVideo(fromFile, toFile, parameter); } /** * 转换成mp4 * * @param fromFile * @param toFile * @param parameter */ public static void convertVideoMp4(String fromFile, String toFile, MediaParameter parameter) throws IOException { // 视频压缩参数初始化 if (parameter == null) { parameter = new MediaParameter(); } parameter.initMp4(); convertVideo(fromFile, toFile, parameter); } /** * 从视频文件中获取第一张图片 * * @param fromFile 视频文件 * @param toFile 转码后保存的文件 * @param parameter 转码后的参数,在转码完成后该参数值会被改变 */ public static void convertVideo(String fromFile, String toFile, MediaParameter parameter) throws IOException { initFile(fromFile, toFile); // 视频压缩参数初始化 if (parameter == null) { parameter = new MediaParameter(); } parameter.check(); FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(fromFile); try { grabber.start(); // 设置视频参数 parameter.init(grabber); Log.info(MediaHelper.class, "开始转换...源文件:%s 目标文件:%s 旋转参数:%s", fromFile, toFile, parameter.toString()); // 定义转码工具 FFmpegFrameRecorder recorder = new FFmpegFrameRecorder( toFile, parameter.getVideoWidthFinally(), parameter.getVideoHeightFinally(), grabber.getAudioChannels() ); parameter.init(grabber, recorder); try { // 转码没有图像 if (Frame) { // 开始转换 // recorder.start(grabber.getFormatContext()); recorder.start(); Frame frame; while ((frame = grabber.grab()) != null) { // 从视频帧中获取图片 if (frame.image != null) { recorder.record(frame); } // 音频帧写入输出流 if (frame.samples != null) { recorder.record(frame); } } } else { // 开始转换 recorder.start(grabber.getFormatContext()); AVPacket packet; while ((packet = grabber.grabPacket()) != null) { recorder.recordPacket(packet); } } } finally { // 设置转码后的视频角度 recorder.stop(); recorder.release(); recorder.close(); } Log.info(MediaHelper.class, "转换完成...源文件:%s 目标文件:%s 旋转参数:%s ", fromFile, toFile, parameter.toString()); } finally { grabber.stop(); grabber.close(); } } private static void initFile(String fromFile, String toFile) { // 源文件判断 File source = new File(fromFile); if (!source.exists()) { throw YzgError.getRuntimeException("039", fromFile); } File target = new File(toFile); if (!target.getParentFile().exists()) { target.getParentFile().mkdirs(); } if (target.exists()) { target.delete(); } } /** * 从视频文件中获取第一张图片 * * @param fromFile 视频文件 * @param toFile 第一帧图片 * @throws IOException */ public static void getVideoFirstImage(String fromFile, String toFile) { try { String suffix = toFile.substring(toFile.lastIndexOf(".") + 1); getGrabberFFmpegImage(fromFile, toFile, suffix, FRAME_INDEX); } catch (IOException ex) { throw YzgError.getRuntimeException(ex, "048", ex.getMessage()); } } /** * 获取视频缩略图 * * @param filePath 视频路径 * @param toFile 存储路径 * @param type 存储类型 * @param index 获取第几帧 * @throws IOException */ public static void getGrabberFFmpegImage(String filePath, String toFile, String type, int index) throws IOException { // 初始化文件 initFile(filePath, toFile); FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(filePath); MediaParameter parameter = new MediaParameter(); try { // 打开视频 grabber.start(); parameter.init(grabber); double rotate = parameter.getVideoRotateFinally(); // 整个视频的长度 int ffLength = grabber.getLengthInFrames(); // 取得能够取得的帧数 index = Math.min(ffLength, index); Frame f; for (int i = 0; i < ffLength; i++) { f = grabber.grabImage(); if (i >= index) { doExecuteFrame(f, toFile, type, rotate); break; } } } finally { grabber.close(); } } /** * 获取视频缩略图 * * @param filePath 视频路径 * @throws IOException */ public static MediaParameter getGrabberParameter(String filePath) throws IOException { FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(filePath); MediaParameter parameter = new MediaParameter(); try { // 打开视频 grabber.start(); parameter.init(grabber); } finally { grabber.close(); } return parameter; } /** * 旋转图片 * * @param src * @param angle * @return */ public static IplImage rotate(IplImage src, double angle) { IplImage img = IplImage.create( src.height(), src.width(), src.depth(), src.nChannels() ); opencv_core.cvTranspose(src, img); opencv_core.cvFlip(img, img, (int) angle); return img; } /** * 截取缩略图 * * @param f 帧 * @param targetFilePath 封面图片 * @param type 封面图片类型 * @param type 旋转角度 */ public static void doExecuteFrame(Frame f, String targetFilePath, String type, double rotate) throws IOException { if (null == f || null == f.image) { return; } Java2DFrameConverter converter = new Java2DFrameConverter(); BufferedImage bi = converter.getBufferedImage(f); if (rotate > 0) { bi = ImageRotate.rotate(bi, (int) rotate); } File output = new File(targetFilePath); ImageIO.write(bi, type, output); } /** * 根据视频长度随机生成随机数集合 * * @param baseNum:基础数字,此处为视频长度 * @param length:随机数集合长度 * @return:随机数集合 */ public static List<Integer> random(int baseNum, int length) { List<Integer> list = new ArrayList<Integer>(length); while (list.size() < length) { Integer next = (int) (Math.random() * baseNum); if (list.contains(next)) { continue; } list.add(next); } Collections.sort(list); return list; } }