springboot 通过 javaCV 实现视频裁剪

文章介绍了如何在Java项目中配置ffmpeg和javacv的相关依赖,包括具体的版本号和平台依赖。核心方法是`videoConvert`,用于视频转分辨率和编码,通过FFmpegFrameGrabber和FFmpegFrameRecorder实现视频的抓取和记录,同时使用FFmpegFrameFilter进行视频过滤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

导入依赖

 	 <properties>
        <java.version>1.8</java.version>

        <ffmpeg.version>0.6.2</ffmpeg.version>
        <javacv.version>1.5.8</javacv.version>
        <javacpp.version>1.5.8</javacpp.version>
        <bytedeco-ffmpeg.version>5.1.2-1.5.8</bytedeco-ffmpeg.version>
        <opencv-platform-gpu.version>4.6.0-1.5.8</opencv-platform-gpu.version>

        <javacpp.platform.dependencies>windows-x86_64</javacpp.platform.dependencies>
        <javacpp.platform.linux-x86_64>linux-x86_64</javacpp.platform.linux-x86_64>

        <!-- 跳过测试 -->
        <skipTests>true</skipTests>
    </properties>
	
	 <!-- maven依赖 -->
	<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

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

        <!-- Optional GPL builds with (almost) everything enabled -->
        <!--        <dependency>-->
        <!--            <groupId>org.bytedeco</groupId>-->
        <!--            <artifactId>ffmpeg-platform-gpl</artifactId>-->
        <!--            <version>5.1.2-1.5.8</version>-->
        <!--        </dependency>-->



        <!--  ========== 优化依赖 ffmpeg-platform-gpl javacv-platform 只导入windows liunx平台 ==============  -->
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacv</artifactId>
            <version>${javacv.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>org.bytedeco</groupId>
                    <artifactId>javacpp</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- 获取视频中的图片 -->
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacpp</artifactId>
            <version>${javacpp.version}</version>
            <classifier>${javacpp.platform.dependencies}</classifier>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacpp</artifactId>
            <version>${javacpp.version}</version>
            <classifier>${javacpp.platform.linux-x86_64}</classifier>
        </dependency>


        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>ffmpeg</artifactId>
            <version>${bytedeco-ffmpeg.version}</version>
            <classifier>linux-x86_64-gpl</classifier>
        </dependency>

        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>ffmpeg</artifactId>
            <version>${bytedeco-ffmpeg.version}</version>
            <classifier>windows-x86_64-gpl</classifier>
        </dependency>

        <dependency>
            <groupId>net.bramp.ffmpeg</groupId>
            <artifactId>ffmpeg</artifactId>
            <version>${ffmpeg.version}</version>
        </dependency>

        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>opencv-platform-gpu</artifactId>
            <version>${opencv-platform-gpu.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

核心方法

实例代码

	/**
     * 视频转分辨率转视频编码
     *
     * @param inputFile   源文件
     * @param outputPath  输出目录
     * @param filterContent  过滤器配置参数
     * @param width       需要转成的视频的宽度
     * @param height      需要转成的视频的高度
     * @param videoFormat 需要转换成的视频格式
     * @return 返回新文件名称,可以根据实际使用修改
     */
    public static String videoConvert(String inputFile, String outputPath,String filterContent, Integer width, Integer height, String videoFormat) {
        List<String> paths = Splitter.on(".").splitToList(inputFile);
        String ext = paths.get(paths.size() - 1);
        if (StringUtils.isEmpty(videoFormat)) {
            videoFormat = ext;
        }
        String newFileName = UUID.randomUUID().toString().replaceAll("-","") + "." + videoFormat;
        String resultPath = Joiner.on(File.separator).join(Arrays.asList(outputPath, newFileName));

        Frame frame;
        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputFile);
        FFmpegFrameRecorder recorder = null;
        double aspectRatio = 1.0;

        FFmpegFrameFilter filter =new FFmpegFrameFilter(filterContent,grabber.getImageWidth(),grabber.getImageHeight());
        try {

            // 初始化帧抓取器,例如数据结构(时间戳、编码器上下文、帧对象等),
            // 如果入参等于true,还会调用avformat_find_stream_info方法获取流的信息,放入AVFormatContext类型的成员变量oc中
            grabber.start(true);
            // grabber.start方法中,初始化的解码器信息存在放在grabber的成员变量oc中

            //过滤器启动
            filter.start();

            AVFormatContext avformatcontext = grabber.getFormatContext();
            // 文件内有几个媒体流(一般是视频流+音频流)
            int streamNum = avformatcontext.nb_streams();
            // 没有媒体流就不用继续了
            if (streamNum < 1) {
                log.info("视频文件格式不对");
                return "error";
            }
            // 取得视频的帧率
            int framerate = (int) grabber.getVideoFrameRate();
            log.info("视频帧率:{},视频时长:{}秒,媒体流数量:{}", framerate, avformatcontext.duration() / 1000000,
                    streamNum);
            // 遍历每一个流,检查其类型
            for (int i = 0; i < streamNum; i++) {
                AVStream avstream = avformatcontext.streams(i);
                AVCodecParameters avcodecparameters = avstream.codecpar();
                log.info("流的索引:{},编码器类型:{},编码器ID:{}", i, avcodecparameters.codec_type(),
                        avcodecparameters.codec_id());
            }
            // 源视频宽度
            int frameWidth = grabber.getImageWidth();
            // 源视频高度
            int frameHeight = grabber.getImageHeight();
            // 源音频通道数量
            int audioChannels = grabber.getAudioChannels();
            log.info("源视频宽度:{},源视频高度:{},音频通道数:{}", frameWidth, frameHeight, audioChannels);


            AVFormatContext ifmt_ctx = grabber.getFormatContext();
            AVStream inpVideoStream = null,
                    inpAudioStream = null;
            if (ifmt_ctx != null) {
                //获取Video的AVStream
                inpVideoStream = ifmt_ctx.streams(grabber.getVideoStream());
                //不能使用 inpVideoStream.sample_aspect_ratio()
                AVRational SAR = inpVideoStream.codecpar().sample_aspect_ratio();
                double a = (double) SAR.num() / (double) SAR.den();
                //默认情况为1:1
                aspectRatio = a == 0.0 ? 1.0 : a;
                int SARden = SAR.den();
                int SARnum = SAR.num();
                log.info("SARden[{}], SARnum[{}]", SARden, SARnum);
            }

            recorder = new FFmpegFrameRecorder(resultPath, width, height, audioChannels);
            // 设置H264编码
//            recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
            // 设置视频格式
            recorder.setFormat(videoFormat);
            // 使用原始视频的码率,若需要则自行修改码率
            recorder.setVideoBitrate(grabber.getVideoBitrate());
            // 一秒内的帧数,帧率
            recorder.setFrameRate(framerate);
            // 两个关键帧之间的帧数
            recorder.setGopSize(framerate);
            // 设置音频通道数,与视频源的通道数相等
            recorder.setAudioChannels(grabber.getAudioChannels());

            //给录制器设置SAR
            recorder.setAspectRatio(aspectRatio);

            recorder.start();
            int videoFrameNum = 0;
            int audioFrameNum = 0;
            int dataFrameNum = 0;
            // 持续从视频源取帧
            while (null != (frame = grabber.grab())) {
                // 有图像,就把视频帧加一
                if (null != frame.image) {
                    videoFrameNum++;
                    //把已经解码后的视频图像像素塞进过滤器
                    filter.push(frame);
                    //取出过滤器处理后的图像像素数据
                    Frame filterFrame =filter.pullImage();
                    // 取出的每一帧,都保存到视频
                    recorder.record(filterFrame);
                }
                // 有声音,就把音频帧加一
                if (null != frame.samples) {
                    audioFrameNum++;
                    // 取出的每一帧
                    recorder.record(frame);
                }
                // 有数据,就把数据帧加一
                if (null != frame.data) {
                    dataFrameNum++;
                }
            }
            log.info("转码完成,视频帧:{},音频帧:{},数据帧:{}", videoFrameNum, audioFrameNum, dataFrameNum);
        } catch (Exception e) {
            log.error(e.getMessage());
            return "error";
        } finally {
            if (recorder != null) {
                try {
                    recorder.close();
                } catch (Exception e) {
                    log.info("recorder.close异常" + e);
                }
            }
            try {
                filter.close();
            } catch (FrameFilter.Exception e) {
                log.info("filter.close异常" + e);
            }
            try {
                grabber.close();
            } catch (FrameGrabber.Exception e) {
                log.info("frameGrabber.close异常" + e);
            }


        }
        return newFileName;
    }

核心类介绍

FFmpegFrameGrabber

JavaCV库中的一个类,它用于从视频文件、摄像头流或网络流中获取帧数据。FFmpegFrameGrabber实际上是基于FFmpeg库构建的,FFmpeg是一个非常强大的跨平台音频/视频处理工具集

主要功能
  • 读取媒体文件:可以从各种格式的视频文件中读取帧。
  • 实时流捕获:能够从网络流(如RTSP、HTTP流等)获取视频帧。
  • 摄像头捕获:可以连接到本地的摄像头设备,实时读取视频帧。
  • 音频处理:除了视频帧之外,也可以处理音频数据
使用方法
  • 初始化:创建FFmpegFrameGrabber实例并设置源
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber("path/to/video.mp4");
  • 启动:调用start()方法来打开视频源
grabber.start();
  • 获取帧:通过调用grabFrame()来获取下一帧
Frame frame = grabber.grabFrame();
  • 读取元数据:可以通过getFormatContext()、getStream()等方法来访问视频的元数据信息。
int frameRate = grabber.getFrameRate();
int imageWidth = grabber.getImageWidth();
int imageHeight = grabber.getImageHeight();
  • 停止:完成操作后,记得关闭资源。
grabber.stop();
属性和方法

FFmpegFrameGrabber提供了许多属性和方法,以支持不同的应用场景,例如:

  • setFormat(String format):设置媒体格式。
  • setDuration(long duration):设置持续时间。
  • getFrameRate():获取帧率。
  • getImageWidth():获取图像宽度。
  • getImageHeight():获取图像高度。
FFmpegFrameRecorder

JavaCV库中的一个类,主要用于将帧序列编码成视频文件或推送到网络流。它是基于FFmpeg库构建的,可以用来创建视频文件或将视频流发送到其他地方(如RTSP服务器)

主要功能
  • 视频编码:将一系列帧编码成视频文件。
  • 音频编码:如果需要的话,也可以处理音频数据。
  • 流式传输:可以将视频流发送到网络上的其他位置。
使用方法
  • 初始化:创建FFmpegFrameRecorder实例并设置目标
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder("output.mp4", width, height);
  • 设置参数:配置视频编码器和其他选项。
recorder.setFormat("mp4"); // 设置输出格式,如MP4
recorder.setVideoCodec(FFmpegFrameRecorder.H264); // 设置视频编解码器,如H264
recorder.setFrameRate(frameRate); // 设置帧率
recorder.setVideoBitrate(bitrate); // 设置比特率
  • 启动:调用start()方法来初始化视频编码器。
recorder.start();
  • 记录帧:通过调用record(Frame frame)来记录每一帧。
recorder.record(frame);
  • 结束录制:完成操作后,记得关闭资源。
recorder.stop(); // 结束录制
recorder.release(); // 释放资源
常用属性和方法
  • setFormat(String format):设置输出格式。
  • setVideoCodec(int codec):设置视频编解码器。
  • setFrameRate(double rate):设置帧率。
  • setVideoBitrate(int bitrate):设置视频比特率。
  • record(Frame frame):记录一帧。
  • stop():停止录制。
  • release():释放资源。

测试方法调用

					//crop=width:height::x:y
			        videoConvert("D:\\testvedio\\1673244765519.mp4","D:\\testvedio\\",MessageFormat.format(
                    "crop={0}:{1}:{2}:{3}",
                    String.valueOf(360),
                    String.valueOf(360),
                    String.valueOf(0),
                    String.valueOf(0)),360,360,"mp4");
3gp视频的分割格式转换视频的分割 import java.awt.Dimension; import java.awt.Frame; import java.awt.Rectangle; import javax.swing.JDialog; import javax.swing.JTextField; public class About extends JDialog { public About(Frame owner, String title, boolean modal) { // 构造方法 super(owner, title, modal); // 继承 this.setLocationRelativeTo(this); this.setSize(240, 260); // 设置大小 this.setDefaultCloseOperation(DISPOSE_ON_CLOSE); // 关闭处理 initUI(); } private void initUI() { this.getContentPane().setLayout(null); jTextField1.setText("我们的口号: 只有想不到,没有做不到"); // 加入文本内容 jTextField1.setEditable(false); jTextField1.setBounds(new Rectangle(1, 29, 239, 41)); // 设置大小 jTextField2.setText("本组成员: 王榆升(组长) 曾佳林 钱彦"); // 加入文本内容 jTextField2.setBounds(new Rectangle(0, 69, 240, 35)); // 设置大小 jTextField2.setEditable(false); jTextField3.setMinimumSize(new Dimension(10, 20)); jTextField3.setEditable(false); jTextField3.setText("版本: 2.3.0"); jTextField3.setBounds(new Rectangle(0, 102, 240, 37)); // 设置大小 jTextField4.setText("最后修定时间: 2008-08-21"); // 加入文本内容 jTextField4.setBounds(new Rectangle(0, 139, 240, 30)); // 设置大小 jTextField4.setEditable(false); jTextField5.setText(""); // 加入文本内容 jTextField5.setBounds(new Rectangle(1, 168, 240, 38)); // 设置大小 jTextField5.setEditable(false); this.getContentPane().add(jTextField1); // 加文本框到容器 this.getContentPane().add(jTextField2); // 加文本框到容器 this.getContentPane().add(jTextField3); // 加文本框到容器 this.getContentPane().add(jTextField4); // 加文本框到容器 this.getContentPane().add(jTextField5); // 加文本框到容器 } JTextField jTextField1 = new JTextField(); // 产生文本录入组件 JTextField jTextField2 = new JTextField(); JTextField jTextField3 = new JTextField(); // 产生文本录入组件 JTextField jTextField4 = new JTextField(); JTextField jTextField5 = new JTextField(); // 产生文本录入组件 }
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yweir

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值