IM开发-视频上传

上传视频,根据视频解析第1帧,获取图像,网上错的比较多,这里记录下,上传视频会要求首帧图片及竖屏处理.

先加上pom依赖

			<dependency>
                <groupId>org.bytedeco</groupId>
                <artifactId>javacv-platform</artifactId>
                <version>1.5.9</version>
            </dependency>

            <!-- Additional dependencies required to use CUDA and cuDNN -->
            <dependency>
                <groupId>org.bytedeco</groupId>
                <artifactId>opencv-platform-gpu</artifactId>
                <version>4.7.0-1.5.9</version>
            </dependency>

            <!-- Optional GPL builds with (almost) everything enabled -->

            <dependency>
                <groupId>org.bytedeco</groupId>
                <artifactId>ffmpeg-platform-gpl</artifactId>
                <version>6.0-1.5.9</version>
            </dependency>

以下是工具类VideoInfoUtils


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.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.File;
import java.math.BigDecimal;
import java.nio.Buffer;
import java.nio.IntBuffer;
import java.util.Map;

/**
 * https://www.jianshu.com/p/50f6a914041f
 * https://blog.csdn.net/wangzhezhilu001/article/details/131655683
 * 获取视频信息
 */
@Slf4j
public class VideoInfoUtils {

    public static void main(String[] args) throws Exception {
        String videoFilePath = "/2024-08-26.mov";
//        videoFilePath = "/Users/aa/Downloads/1724918704fb2d4b4f848a4b93e0f7c5de73d6c9a0.mp4";
        File videoFile = new File(videoFilePath);

        VideoInfo videoInfo = getVideoInfo(videoFile);
        log.info("videoInfo:{}", videoInfo);

        String targetImgPath = "/Users/aa/Downloads/cover3.jpg";
        File targetFile = new File(targetImgPath);
        boolean tempPath = generateFirstFrameFace(targetFile, videoFile, videoInfo.getRotation());

    }

    public static VideoInfo getVideoInfo(File file) {
        VideoInfo videoInfo = new VideoInfo();
        FFmpegFrameGrabber grabber = null;
        try {
            grabber = new FFmpegFrameGrabber(file);
            // 启动 FFmpeg
            grabber.start();
            Map<String, Buffer> videoSideData = grabber.getVideoSideData();
            // 横拍的视频, {Display Matrix=java.nio.DirectByteBuffer[pos=0 lim=36 cap=36]}
            log.info("VideoSideData:{}", videoSideData);
            Buffer displayMatrixBuffer = videoSideData.get("Display Matrix");
            if (displayMatrixBuffer != null) {
                java.nio.MappedByteBuffer directByteBuffer = (java.nio.MappedByteBuffer) displayMatrixBuffer;
                IntBuffer displayMatrixIntBuffer = directByteBuffer.asIntBuffer();
                // 创建 int[] 数组并复制数据
                int[] displayMatrix = new int[displayMatrixIntBuffer.limit()];
                displayMatrixIntBuffer.get(displayMatrix);
                float rotation = calculateRotation(displayMatrix);
                log.info("rotation:{}", rotation);
                videoInfo.setRotation(rotation);
            }

            // 读取视频帧数
            videoInfo.setLengthInFrames(grabber.getLengthInVideoFrames());

            // 读取视频帧率


            videoInfo.setFrameRate(grabber.getVideoFrameRate());

            // 读取视频秒数
            videoInfo.setDuration(new BigDecimal(grabber.getLengthInTime()).divide(new BigDecimal("1000000")).intValue());

            // 读取视频宽度
            videoInfo.setWidth(grabber.getImageWidth());

            // 读取视频高度
            videoInfo.setHeight(grabber.getImageHeight());


            videoInfo.setAudioChannel(grabber.getAudioChannels());

            videoInfo.setVideoCode(grabber.getVideoCodecName());

            videoInfo.setAudioCode(grabber.getAudioCodecName());
            // String md5 = MD5Util.getMD5ByInputStream(new FileInputStream(file));

            videoInfo.setSampleRate(grabber.getSampleRate());
            videoInfo.setFormat(grabber.getFormat());

            return videoInfo;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (grabber != null) {
                    // 此处代码非常重要,如果没有,可能造成 FFmpeg 无法关闭
                    grabber.stop();
                    grabber.release();
                }
            } catch (FFmpegFrameGrabber.Exception e) {
                log.error("getVideoInfo grabber.release failed 获取文件信息失败:{}", e.getMessage());
            }
        }
    }


    /**
     * 计算旋转角度
     * @param displayMatrix
     * @return
     */
    private static float calculateRotation(int[] displayMatrix) {
        if (displayMatrix.length != 9) {
            throw new IllegalArgumentException("Display Matrix should have 9 elements.");
        }

        // 解析旋转角度
        // a = displayMatrix[0], b = displayMatrix[1], d = displayMatrix[3], e = displayMatrix[4]
        float a = displayMatrix[0], b = displayMatrix[1], d = displayMatrix[3], e = displayMatrix[4];

        double radians = Math.atan2(b, a); // 逆时针角度
        double degrees = Math.toDegrees(radians);

        // 确保角度范围在 [0, 360) 之间
        degrees = degrees < 0 ? 360 + degrees : degrees;
        return (float) degrees;
    }

    /**
     * 生成视频的首帧图片
     * @throws
     * @Title: getTempPath
     * @param: @param tempPath 生成首帧图片的文件地址
     * @param: @param filePath 传进来的线上文件
     * @param: @return
     * @param: @throws Exception
     * @return: boolean
     */
    public static boolean generateFirstFrameFace(File targetFile, File videoFile, float rotation) throws Exception {
        FFmpegFrameGrabber ff = new FFmpegFrameGrabber(videoFile);
        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, rotation), "jpg", targetFile)) {
            ff.close();
            ff.stop();
            log.info("输出图片成功!");
            return true;
        } else {
            ff.close();
            ff.stop();
            log.info("输出图片失败!");
            return false;
        }
    }

    /***
     *
     * @Title: FrameToBufferedImage
     * @Description: 创建格式化BufferedImage对象
     * @param: @param frame
     * @param: @return
     * @return: RenderedImage
     * @throws
     */
    private static RenderedImage FrameToBufferedImage(Frame frame, float rotation) {
        //创建BufferedImage对象
        Java2DFrameConverter converter = new Java2DFrameConverter();
        BufferedImage bufferedImage = converter.getBufferedImage(frame);
        if (rotation > 0) {
            log.info("旋转图片");
            bufferedImage = rotateImage(bufferedImage, rotation);
        }
        return bufferedImage;
    }

    public static BufferedImage rotateImage(BufferedImage originalImage, double angle) {
        // 计算旋转角度的弧度
        double radians = Math.toRadians(angle);

        // 获取原始图像的宽度和高度
        int originalWidth = originalImage.getWidth();
        int originalHeight = originalImage.getHeight();

        // 计算旋转后图像的宽度和高度
        int rotatedWidth = (int) Math.abs(originalWidth * Math.cos(radians)) + (int) Math.abs(originalHeight * Math.sin(radians));
        int rotatedHeight = (int) Math.abs(originalHeight * Math.cos(radians)) + (int) Math.abs(originalWidth * Math.sin(radians));

        // 创建一个新的图像对象,大小为旋转后的宽度和高度
        BufferedImage rotatedImage = new BufferedImage(rotatedWidth, rotatedHeight, originalImage.getType());

        // 获取旋转变换对象
        AffineTransform transform = new AffineTransform();
        transform.translate((rotatedWidth - originalWidth) / 2, (rotatedHeight - originalHeight) / 2);
        transform.rotate(radians, originalWidth / 2.0, originalHeight / 2.0);

        // 使用 Graphics2D 对象进行图像旋转
        Graphics2D g2d = rotatedImage.createGraphics();
        g2d.setTransform(transform);
        g2d.drawImage(originalImage, 0, 0, null);
        g2d.dispose();

        return rotatedImage;
    }
}

VideoInfo 的定义

@Getter
@Setter
@ToString
public class VideoInfo {
    /**
     * 总帧数
     **/
    private int lengthInFrames;

    /**
     * 帧率
     **/
    private double frameRate;

    /**
     * 时长
     **/
    private int duration;

    /**
     * 视频编码


     */
    private String videoCode;
    /**
     * 音频编码
     */
    private String audioCode;

    private int width;
    private int height;
    private int audioChannel;
    private String md5;
    /**
     * 音频采样率
     */
    private Integer sampleRate;

    /**
     * 视频格式
     */
    private String format;

    /**
     * 视频旋转角度
     */
    private float rotation;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值