首页 > 编程语言 >JavaFX+JavaCV实现批量视频处理及批量生成视频开发笔记--003,批量视频混剪功能设计与代码实现

JavaFX+JavaCV实现批量视频处理及批量生成视频开发笔记--003,批量视频混剪功能设计与代码实现

时间:2024-10-24 19:20:16浏览次数:5  
标签:视频 批量 -- 30 add grid new import

我要使用JavaFX+JavaCV实现一个桌面应用,可以打包成Windows和Mac的桌面应用。
实现的功能是:批量视频混剪。具体操作是:在界面上选择一个文件夹或多个视频文件,对文件夹中的所有视频文件(仅.mp4格式)或者选中的文件进行处理,随机截取原视频中指定长度的视频片段(如5秒),拼接成多个新的视频。其中:截取原视频的指定长度,是可以在界面上选择的(1到30秒),生成多少个新视频也是可以在界面上选择的(1到30个)。
多个视频片段合并成新视频时,使用JavaCV过滤器实现过渡动画效果。下面是一个详细的设计思路,包括所需知识点、核心类和示例代码。

你要问这个功能能做什么,嘿嘿,批量生成横屏、竖屏的搞笑视频混剪、小姐姐跳舞视频混剪、各种无厘头视频混剪等等,可以使用JavaCV实现你想要的各种特效:加字幕、过渡效果、多视频分屏、画中画等,只有你想不到的,没有JavaCV做不到的,哈哈。

设计思路

  1. 用户界面设计

    • 使用 JavaFX 创建一个图形用户界面,包含文件选择、参数输入和处理按钮。
    • 提供选择文件夹或多个视频文件的功能。
    • 提供输入框让用户选择视频片段的长度(1到30秒)和生成的新视频数量(1到30个)。
    • 提供处理按钮,触发视频处理逻辑。
  2. 视频处理逻辑

    • 使用 JavaCV 处理视频文件,包括读取、截取、拼接和添加过渡效果。
    • 随机截取指定长度的视频片段。
    • 使用 JavaCV 过滤器实现视频片段之间的过渡动画效果。
  3. 打包和发布

    • 使用 Maven 或 Gradle 构建项目,确保所有依赖项正确引入。
    • 使用工具如 JPackage 或 Inno Setup 打包成 Windows 和 Mac 的可执行文件。

完整Gitee地址:

使用到的知识点及核心类

  • JavaFX

    • Stage:主窗口。
    • Scene:场景。
    • Button:按钮。
    • TextField:输入框。
    • FileChooser:文件选择对话框。
    • DirectoryChooser:目录选择对话框。
    • Alert:提示对话框。
  • JavaCV

    • FFmpegFrameGrabber:读取视频文件。
    • FFmpegFrameRecorder:录制视频文件。
    • Frame:表示视频帧。
    • FrameFilter:视频过滤器,用于实现过渡效果。
    • OpenCVFrameConverter:帧转换器。

在这里插入图片描述

示例代码

1. 项目结构
src
├── main
│   ├── java
│   │   └── com
│   │       └── example
│   │           └── VideoEditorApp.java
│   └── resources
2. VideoEditorApp.java
package com.example;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import org.bytedeco.javacv.*;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class VideoEditorApp extends Application {

    private List<File> videoFiles = new ArrayList<>();
    private TextField durationField;
    private TextField countField;

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("视频混剪工具");

        GridPane grid = new GridPane();
        grid.setHgap(10);
        grid.setVgap(10);
        grid.setPadding(new Insets(20, 20, 20, 20));

        Label folderLabel = new Label("选择文件夹或视频文件:");
        grid.add(folderLabel, 0, 0);

        Button folderButton = new Button("选择文件夹");
        folderButton.setOnAction(e -> selectFolder());
        grid.add(folderButton, 1, 0);

        Button fileButton = new Button("选择文件");
        fileButton.setOnAction(e -> selectFiles());
        grid.add(fileButton, 2, 0);

        Label durationLabel = new Label("视频片段长度 (1-30秒):");
        grid.add(durationLabel, 0, 1);

        durationField = new TextField();
        grid.add(durationField, 1, 1);

        Label countLabel = new Label("生成新视频数量 (1-30):");
        grid.add(countLabel, 0, 2);

        countField = new TextField();
        grid.add(countField, 1, 2);

        Button processButton = new Button("开始处理");
        processButton.setOnAction(e -> processVideos());
        grid.add(processButton, 1, 3);

        Scene scene = new Scene(grid, 400, 200);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private void selectFolder() {
        DirectoryChooser directoryChooser = new DirectoryChooser();
        File selectedDirectory = directoryChooser.showDialog(primaryStage);
        if (selectedDirectory != null) {
            videoFiles.clear();
            for (File file : selectedDirectory.listFiles((dir, name) -> name.endsWith(".mp4"))) {
                videoFiles.add(file);
            }
        }
    }

    private void selectFiles() {
        FileChooser fileChooser = new FileChooser();
        fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("MP4 Files", "*.mp4"));
        List<File> selectedFiles = fileChooser.showOpenMultipleDialog(primaryStage);
        if (selectedFiles != null) {
            videoFiles.addAll(selectedFiles);
        }
    }

    private void processVideos() {
        int duration = Integer.parseInt(durationField.getText());
        int count = Integer.parseInt(countField.getText());

        if (duration < 1 || duration > 30 || count < 1 || count > 30) {
            Alert alert = new Alert(Alert.AlertType.ERROR, "请输入有效的参数!");
            alert.showAndWait();
            return;
        }

        Random random = new Random();
        for (int i = 0; i < count; i++) {
            List<Frame> frames = new ArrayList<>();
            for (File file : videoFiles) {
                FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(file.getAbsolutePath());
                try {
                    grabber.start();
                    long startTime = random.nextInt((int) (grabber.getLengthInTime() - duration * 1000000));
                    grabber.setTimestamp(startTime);
                    for (int j = 0; j < duration * 30; j++) { // 假设每秒30帧
                        Frame frame = grabber.grab();
                        if (frame != null) {
                            frames.add(frame);
                        }
                    }
                    grabber.stop();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            FFmpegFrameRecorder recorder = new FFmpegFrameRecorder("output_" + i + ".mp4", 640, 480);
            try {
                recorder.setFormat("mp4");
                recorder.setFrameRate(30);
                recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
                recorder.start();

                for (int j = 0; j < frames.size(); j++) {
                    if (j > 0 && j % 30 == 0) { // 每1秒添加一次过渡效果
                        addTransitionEffect(recorder, frames.get(j - 1), frames.get(j));
                    }
                    recorder.record(frames.get(j));
                }

                recorder.stop();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        Alert alert = new Alert(Alert.AlertType.INFORMATION, "处理完成!");
        alert.showAndWait();
    }

    private void addTransitionEffect(FFmpegFrameRecorder recorder, Frame from, Frame to) throws Exception {
        FrameFilter filter = new FrameFilter("blend=all_mode='addition':all_opacity=0.5", 640, 480);
        Frame blendedFrame = filter.push(from);
        blendedFrame = filter.pull();
        recorder.record(blendedFrame);
    }

    public static void main(String[] args) {
        launch(args);
    }
}

依赖管理

pom.xml 文件中添加 JavaFX 和 JavaCV 的依赖:

<dependencies>
    <!-- JavaFX -->
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-controls</artifactId>
        <version>17.0.1</version>
    </dependency>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-fxml</artifactId>
        <version>17.0.1</version>
    </dependency>

    <!-- JavaCV -->
    <dependency>
        <groupId>org.bytedeco</groupId>
        <artifactId>javacv-platform</artifactId>
        <version>1.5.7</version>
    </dependency>
</dependencies>

打包和发布

  1. 使用 Maven 打包

    • 添加 maven-assembly-plugin 插件到 pom.xml,配置打包任务。
    • 运行 mvn clean package 生成可执行 JAR 文件。
  2. 使用 JPackage 打包

    • 安装 JDK 14+,使用 jpackage 工具将 JAR 文件打包成 Windows 或 Mac 的可执行文件。

总结

以上是一个基于 JavaFX 和 JavaCV 的视频混剪工具的完整实现。通过这个工具,用户可以选择文件夹或多个视频文件,设置视频片段的长度和生成的新视频数量,然后进行视频处理。视频处理逻辑包括随机截取指定长度的视频片段、拼接视频片段并添加过渡效果。

标签:视频,批量,--,30,add,grid,new,import
From: https://blog.csdn.net/qq_41089021/article/details/143106817

相关文章

  • python实战(二)——房屋价格回归建模
    一、任务背景    本章将使用一个经典的Kaggle数据集——HousePrices-AdvancedRegressionTechniques进行回归建模的讲解。这是一个房价数据集,与我们熟知的波士顿房价数据集类似,但是特征数量要更多,数据也要更为复杂一些。下面,我们将使用这个房价数据进行机器学习中......
  • 解决数组两数之和问题与逻辑推理找出谋杀案凶手
    给定一个整数数组nums和一个整数目标值target(2<=nums.length<=10^4),请你在该数组中找出和为目标值target的那两个整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。你可以按任意顺序返回答案。示例1:输入:nums=[2,7,11,15],t......
  • 微信小程序中组件通信的性能优化方法有哪些?
    减少不必要的数据传递原理:组件间传递的数据量越小,通信的开销就越小。每次数据传递都涉及到数据的序列化、传输和反序列化等过程,过多或过大的数据传递会增加这些操作的频率和资源消耗。示例:比如在父子组件通信中,如果子组件只需要使用父组件中一个数据字段的部......
  • 基于neo4j的学术论文关系管理系统
    正在为毕业设计头疼?又或者在学术研究中总是找不到像样的工具来管理浩瀚的文献资料?今天给大家介绍一款超实用的工具——基于Neo4j的学术论文关系管理系统,让你轻松搞定学术文献的管理与展示!......
  • 如何在微信小程序中使用事件总线进行组件通信?
    创建事件总线(EventBus)模块目的:事件总线是一个独立的模块,用于管理事件的发布和订阅。它提供了一个集中的机制,使得组件之间可以通过发布和订阅事件来进行通信,而不需要依赖组件之间的父子关系或其他复杂的层级结构。代码实现:创建一个名为event-bus.js的文件,......
  • 如何避免在微信小程序中使用事件总线进行组件通信时出现内存泄漏?
    理解内存泄漏问题的产生原因在微信小程序中使用事件总线进行组件通信时,内存泄漏可能是由于组件在销毁后仍然被事件总线持有引用,导致无法被垃圾回收机制正常回收。例如,组件订阅了事件总线的某个事件,当组件被销毁时,如果没有正确地取消订阅,那么事件总线中仍然保存着对该组件......
  • 基于neo4j的疫情信息管理系统
    你是否想过,一个能清晰展示疫情传播路径和患者关系的系统有多强大?今天,就来介绍一套专为疫情信息设计的知识图谱管理系统,它利用Neo4j图数据库构建,帮助你轻松掌握疫情动态和患者之间的潜在联系,让疫情防控不再复杂。......
  • Linux服务器上有挖矿病毒处理案例记录
    症状表现服务器CPU资源使用一直处于100%的状态,通过top命令查看,发现可疑进程kdevtmpfsi。通过百度搜索,发现这是挖矿病毒。排查方法首先:查看kdevtmpfsi进程,使用ps-ef|grepkdevtmpfsi命令查看,见下图。PS:通过ps-ef命令查出kdevtmpfsi进程号,直接kill-9进......
  • 【leetcode-面试经典 150 题】-4.删除有序数组中的重复项 II
    题目:给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用O(1)额外空间的条件下完成。示例1:输入:nums=[1,1,1,2,2,3]输出:5,nums=......
  • 使用PyInstaller将Python代码打包为.exe可执行程序(一)
    一、简介PyInstaller是一个用于将Python程序打包成独立可执行文件(如.exe文件用于Windows系统、.app文件用于Mac系统等)的第三方库。它能够把Python脚本及其所依赖的库文件、资源文件等打包到一个单独的文件中,这样可以方便地将程序分发给其他用户,而无需用户在其机器......