java实现一个录像大师
java实现一个录像大师
javacv从入门到入土系列,发现了个好玩的东西,视频处理,于是我想搞了个屏幕录屏大师,这里我使用javafx进行页面显示。

依赖
<!-- 需要注意,javacv主要是一组API为主,还需要加入对应的实现 -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv</artifactId>
<version>1.5.6</version>
</dependency>
<!-- 用到了 ffmpeg 需要把 ffmpeg 的平台实现依赖引入 -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg-platform</artifactId>
<version>4.4-1.5.6</version>
</dependency>
<!--所有平台实现,依赖非常大,几百MB吧-->
<!--<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.6</version>
</dependency>-->
实现
package top.lingkang.test.gui;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import org.bytedeco.ffmpeg.avcodec.AVPacket;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.*;
import javax.sound.sampled.*;
import java.io.File;
import java.util.Timer;
import java.util.TimerTask;
/**
* @author lingkang
* Created by 2022/5/10
*/
public class DesktopRecording extends Application {
private static final int frameRate = 10;// 录制的帧率
private static boolean isStop = false;
private static TargetDataLine line;
private static File audioFile, videoFile;
private static ImageView imageVideo;
private static boolean isStart;
@Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("lingkang-桌面录屏emm...");
imageVideo = new ImageView();
imageVideo.setFitWidth(800);
imageVideo.setFitHeight(600);
Button button = new Button("停止录制");
button.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
isStop = true;
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("info");
alert.setHeaderText("已经停止录制");
alert.setOnCloseRequest(event1 -> alert.hide());
alert.showAndWait();
}
});
VBox box = new VBox();
box.getChildren().addAll(button, imageVideo);
primaryStage.setScene(new Scene(box));
primaryStage.setHeight(600);
primaryStage.setWidth(800);
primaryStage.show();
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
isStop = true;
System.exit(0);
}
});
// window 建议使用 FFmpegFrameGrabber("desktop") 进行屏幕捕捉
FrameGrabber grabber = new FFmpegFrameGrabber("desktop");
grabber.setFormat("gdigrab");
grabber.setFrameRate(frameRate);
// 捕获指定区域,不设置则为全屏
grabber.setImageHeight(600);
grabber.setImageWidth(800);
//grabber.setOption("offset_x", "200");
grabber.setOption("offset_y", "200");//必须设置了大小才能指定区域起点,参数可参考 FFmpeg 入参
grabber.start();
videoFile = File.createTempFile("lingkang", ".avi");
// 用于存储视频 , 先调用stop,在释放,就会在指定位置输出文件,,这里我保存到D盘
FFmpegFrameRecorder recorder = FFmpegFrameRecorder.createDefault(videoFile, grabber.getImageWidth(), grabber.getImageHeight());
recorder.setVideoOption("tune", "zerolatency");
recorder.setFrameRate(frameRate);
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);// 编码,使用编码能让视频占用内存更小,根据实际自行选择
recorder.start();
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
if (isStop) {
try {
// 停止
recorder.stop();
grabber.stop();
// 释放内存,我们都知道c/c++需要手动释放资源
recorder.release();
grabber.release();
} catch (Exception e) {
e.printStackTrace();
}
new Thread(new Runnable() {
@Override
public void run() {
try {
composeVideo();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
timer.cancel();
return;
}
if (!isStart) {
isStart = true;
// 录制声音
startAudio();
}
try {
// 获取屏幕捕捉的一帧
Frame frame = grabber.grabFrame();
// 将这帧放到记录
recorder.record(frame);
// 观察可以怎么实现的
Image convert = new JavaFXFrameConverter().convert(frame);
// 更新
imageVideo.setImage(convert);
} catch (Exception e) {
e.printStackTrace();
}
}
}, 2000, 1000 / frameRate);
}
/**
* 音视频合并为一个视频
*/
private void composeVideo() throws Exception {
// 合成视频
FFmpegFrameGrabber imageGrabber = new FFmpegFrameGrabber(videoFile);
imageGrabber.start();
FFmpegFrameGrabber audioGrabber = new FFmpegFrameGrabber(audioFile);
audioGrabber.start();
File file = new File("D://video666666.avi");
if (file.exists())
file.delete();
// 流媒体输出地址,分辨率(长,高),是否录制音频(0:单声道/1:立体声)
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(file, imageGrabber.getImageWidth(),
imageGrabber.getImageHeight(), 1);
recorder.setInterleaved(true);
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
recorder.start(imageGrabber.getFormatContext());
long videoTime = imageGrabber.getLengthInTime();
AVPacket packet = null;
for (; (packet = imageGrabber.grabPacket()) != null; ) {
recorder.recordPacket(packet);
}
Frame sampleFrame = null;
while ((sampleFrame = audioGrabber.grabSamples()) != null) {
recorder.record(sampleFrame);
if (audioGrabber.getTimestamp() >= videoTime) {
break;
}
}
recorder.close();
audioGrabber.close();
imageGrabber.close();
System.out.println("视频合成完毕!");
}
private void startAudio() {
// 录音
new Thread(new Runnable() {
@Override
public void run() {
try {
AudioFormat audioFormat = new AudioFormat(44100.0F, 16, 2, true, false);
DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class, audioFormat);
// 可以捕捉不同声道
line = (TargetDataLine) AudioSystem.getLine(dataLineInfo);
line.open(audioFormat);
line.start();
audioFile = File.createTempFile(String.valueOf(System.currentTimeMillis()), ".wav");
AudioInputStream ais = new AudioInputStream(line);
// start recording
AudioSystem.write(ais, AudioFileFormat.Type.WAVE, audioFile);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
public static void main(String[] args) throws Exception {
launch(args);
}
}
java实现一个录像大师的更多相关文章
- paip.操作符重载的缺失 Java 的一个大缺点
paip.操作符重载的缺失 Java 的一个大缺点 #----操作符重载的作用 1.提升用户体验 操作符重载..可以让代码更加自然.... 2.轻松实现代码代码移植 例如 java代码会直接移植到 ...
- 使用Java编写一个简单的Web的监控系统cpu利用率,cpu温度,总内存大小
原文:http://www.jb51.net/article/75002.htm 这篇文章主要介绍了使用Java编写一个简单的Web的监控系统的例子,并且将重要信息转为XML通过网页前端显示,非常之实 ...
- java 多线程——一个定时调度的例子
java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...
- java中将一个字符数组赋值给另一个,两者同时变化
java中将一个字符数组赋值给另一个,两者的变化怎么是同步的?怎么才能让他们独立开? 比如有一个int[][] a 已经存在值,现在定义int[][] b=a;之后改变a的值,为何b也跟着改变?怎么才 ...
- 用JAVA写一个函数,功能例如以下: 随意给定一组数, 找出随意数相加之后的结果为35(随意设定)的情况
用JAVA写一个函数.功能例如以下:随意给定一组数,比如{12,60,-8,99,15,35,17,18},找出随意数相加之后的结果为35(随意设定)的情况. 能够递归算法来解: package te ...
- 为什么用 Java:一个 Python 程序员告诉你
这篇文章专门给程序员写的,普通读者慎入.原作者:Kevin Sookocheff 译者:Celia Zhen,原文点击文末链接. 每当我告诉别人我一直在用Java工作时,大家的反应都是: “纳尼!Ja ...
- 五:用JAVA写一个阿里云VPC Open API调用程序
用JAVA写一个阿里云VPC Open API调用程序 摘要:用JAVA拼出来Open API的URL 引言 VPC提供了丰富的API接口,让网络工程是可以通过API调用的方式管理网络资源.用程序和软 ...
- 用java写一个servlet,可以将放在tomcat项目根目录下的文件进行下载
用java写一个servlet,可以将放在tomcat项目根目录下的文件进行下载,将一个完整的项目进行展示,主要有以下几个部分: 1.servlet部分 Export 2.工具类:TxtFileU ...
- 用Java开发一个工具类,提供似于js中eval函数功能的eval方法
今天在看到<Java疯狂讲义>中一个章节习题: 开发一个工具类,该工具类提供一个eval()方法,实现JavaScript中eval()函数的功能--可以动态运行一行或多行程序代码.例如: ...
- Java判断一个字符是否是数字的几种方法的代码
在工作期间,将写内容过程经常用到的一些内容段做个记录,下面内容是关于Java判断一个字符是否是数字的几种方法的内容,希望能对码农们有好处. public class Test{ public stat ...
随机推荐
- python实现简单的爬虫功能
前言Python是一种广泛应用于爬虫的高级编程语言,它提供了许多强大的库和框架,可以轻松地创建自己的爬虫程序.在本文中,我们将介绍如何使用Python实现简单的爬虫功能,并提供相关的代码实例. 如何实 ...
- 利用SPI实现全自动化——LCD屏与RGB灯
如果你开启了广告屏蔽,请将博客园加入白名单,帮助博客园渡过难关,谢谢! 前言 在21年做物理实验和23年客串电赛之后,我带着STM32重回电子DIY界.这次的项目是一个电池供电的补光灯,由于用途更偏向 ...
- Dubbo3应用开发—Dubbo服务管理平台DubboAdmin介绍、安装、测试
Dubbo服务管理平台 DubboAdmin的介绍 Dubbo Admin是Apache Dubbo服务治理和管理系统的一部分. Dubbo Admin提供了一套用于服务治理的Web界面,让我们可以更 ...
- CIC滤波器仿真与实验过程及结果记录
整理于2023-10-08 0.0 前言: 前面介绍了使用matlab中的Filter Designer工具箱进行CIC抽取滤波器设计的仿真过程与结果.下面在前面的基础上针对现有的[正点原子ZYNQ] ...
- 有关library导入的个人总结和反思
本来帮助朋友找寻一下android的一些特效的demo,结果找到了一个,朋友试验可以,自己却是在导入项目需要的library的时候总是出问题,真的很是丢人,反省反省. 也许专业人士看来这是非常可笑的问 ...
- Flink测试利器之DataGen初探
什么是 Flinksql Flink SQL 是基于 Apache Calcite 的 SQL 解析器和优化器构建的,支持ANSI SQL 标准,允许使用标准的 SQL 语句来处理流式和批处理数据.通 ...
- 14.9 Socket 高效文件传输
网络上的文件传输功能也是很有必要实现一下的,网络传输文件的过程通常分为客户端和服务器端两部分.客户端可以选择上传或下载文件,将文件分块并逐块发送到服务器,或者从服务器分块地接收文件.服务器端接收来自客 ...
- pbootcms 后台内容列表搜索功能扩展及增加显示字段功能
应项目要求,一个内容模型下栏目不宜分的层级过多,如新闻模块,分2022.2023.2024年度,每年度下分12个月,这样就是2层栏目,再依类别(科技.动漫.电影...)划分层级,栏目数量较多,而且不易 ...
- CodeChef Starters 9 Division 3 (Rated) India Fights Corona
原题链接 India Fights Corona 题意: 有\(n\)个城市,\(m\)条道路,其中有些城市自己有医院,所以可以在自己城市做核酸检测,那么花费就只有就医费用,而对于那些自己没有医院的城 ...
- 线性代数导论MIT第一章中的知识点
线性代数知识点: 0.矩阵表 1.线性组合 2.向量与线性组合 3.线性组合 4.三维向量 平面向量(x, y)与3维空间的(x, y, 0)不相同! 二维空间的向量v具有两个分量v1与v2. v + ...