IM-上传视频

上传视频,根据视频解析第1帧,获取图像,网上错的比较多;

<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>

先加上pom依赖, 以下是工具类

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

* 获取视频信息

* @author

*/

@Slf4j

public class VideoInfoUtils {

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

String videoFilePath ="/XXX/2024-08-26.mov";

//        videoFilePath = "/XX/3e0f7c5de73d6c9a0.mp4";

        File videoFile =new File(videoFilePath);

        VideoInfo videoInfo =getVideoInfo(videoFile);

        log.info("videoInfo:{}", videoInfo);

        String targetImgPath ="/XX/cover3.jpg";

        File targetFile =new File(targetImgPath);

        boolean tempPath =generateFirstFrameFace(targetFile, videoFile, videoInfo.getRotation());

    }

public static VideoInfogetVideoInfo(File file) {

VideoInfo videoInfo =new VideoInfo();

        FFmpegFrameGrabber grabber =null;

        try {

grabber =new FFmpegFrameGrabber(file);

            // 启动 FFmpeg

            grabber.start();

            Map 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 RenderedImageFrameToBufferedImage(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 BufferedImagerotateImage(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;

    }

}


再者是返回的结果对象:

@Getter

@Setter

@ToString

public class VideoInfo {

/**

* 总帧数

**/

    private int lengthInFrames;

    /**

* 帧率

**/

    private double frameRate;

    /**

* 时长

**/

    private int duration;

    /**

* 视频编码

*/

    private StringvideoCode;

    /**

* 音频编码

*/

    private StringaudioCode;

    private int width;

    private int height;

    private int audioChannel;

    private Stringmd5;

    /**

* 音频采样率

*/

    private IntegersampleRate;

    /**

* 视频格式

*/

    private Stringformat;

    /**

* 视频旋转角度

*/

    private float rotation;

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352

推荐阅读更多精彩内容