使用JAVAFX模块写的GUI程序,供学习使用。
先上一下动态效果图展示,下面分别是主界面和菜单界面,实现的功能有转GIF(需要用到ffmpeg),压缩视频,定时录屏等。
代码部分
public final class VideoRecode extends Application {
private double start_x;
private double start_y;
private double w;
private double h;
private double xOffSet = 0;
private double yOffSet = 0;
private Stage primaryStage;
private FlowPane[] flowPanes;
private volatile boolean isstop = false;
private ToggleGroup group = new ToggleGroup();
private Alert alerts = new Alert(AlertType.CONFIRMATION);
private TextField save = new TextField();
private DirectoryChooser fileChooser = new DirectoryChooser();
private ScheduledExecutorService services0,services2;
private Rectangle rectangle = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
private BorderPane borderPane;
private ListView<Node> listview = new ListView<Node>();
private ProgressBar slider = new ProgressBar(10.0);
// 视频录制区域
private String filenames;
private int sampleRate, numChannels;
byte[] audioBytes;
private Java2DFrameConverter java2dConverter = new Java2DFrameConverter();
private FFmpegFrameRecorder recorder;
private Robot robot;
private TargetDataLine line;
private AudioFormat audioFormat;
private DataLine.Info dataLineInfo;
private long startTime = 0;
private long videoTS = 0;
private long pauseTime = 0;
private String[][] boxtxt1 = { { "视频比特率", "10000", "50000", "1000000", "2000000" },
{ "压缩方式", "ultrafast(终极快)", "superfast(超级快)", "veryfast(非常快)", "faster(很快)", "fast(快)", "medium(中等)",
"slow(慢)", "slower(很慢)", "veryslow(非常慢)" },
{ "音频通道", "1", "2" }, { "音频采样率", "32000Hz", "44100Hz", "48000Hz", "50000Hz" } };
private Map<String, Object> OPTIONS = new LinkedHashMap<String, Object>() {
private static final long serialVersionUID = 1L;
{
put("frameRate",9);
put("samplerate",32000);
put("nmber",0);
put("bitrate",10000);
put("preset","veryslow");
put("channel",1);
put("isaudio", false);
put("isshutdown", false);
put("isplay", false);
put("isyasuo", false);
put("iszhuan", false);
put("isgif", false);
put("isopen", false);
}
};
private Stage stages = getMenu();
private Button[] buttonss;
@SuppressWarnings("null")
@Override
public void init() throws Exception {
// HBox[] hBoxs = Stream.of("视频来源4512540","视频来源4512540").map(a -> {
// Label label = new Label(a);
// label.setStyle("-fx-font-size:16");
// Label label2 = new Label("打开位置");
// label2.setCursor(Cursor.HAND);
// label2.setStyle("-fx-font-size:16");
// HBox hBox2 = new HBox(20,label,label2);
// hBox2.setPadding(new Insets(10));
// hBox2.setAlignment(Pos.CENTER);
// return hBox2;
// }).toArray(HBox[]::new);
//slider.setAccessibleText("");
slider.setPrefSize(300, 10);
alerts.setTitle("系统提示");
alerts.setHeaderText("系统消息");
//listview.setItems(FXCollections.observableArrayList(hBoxs));
File files = new File(FileSystemView.getFileSystemView().getHomeDirectory(), "录屏目录");
if (!files.exists())
files.mkdirs();
save.setEditable(false);
save.setText(files.getPath());
fileChooser.setInitialDirectory(new File("D:\\桌面"));
Label[] labels = Stream.of("录屏工具", "00:00:00").map(a -> {
Label label = new Label(a);
if (Objects.equals("录屏工具", a)) {
label.setStyle("-fx-font-size:18;-fx-text-fill:white");
} else {
label.setStyle("-fx-font-family:\"DigifaceWide\";-fx-font-size:60;-fx-text-fill:black;-fx-font-weight:bold");
}
return label;
}).toArray(Label[]::new);
this.buttonss = Stream.of("开始","暂停").map(s -> {
Button buttons = new Button(s);
double w = 100, h = 100;
buttons.setPrefSize(w, h);
javafx.scene.shape.Rectangle rectangle = new javafx.scene.shape.Rectangle(w, h);
rectangle.setArcWidth(w);
rectangle.setArcHeight(h);
buttons.setCursor(Cursor.HAND);
buttons.setClip(rectangle);
buttons.setStyle("-fx-font-weight:bold;-fx-font-size:30;-fx-text-fill:white;-fx-background-color:#0096ff");
buttons.setOnAction(b -> {
AtomicInteger atomic = new AtomicInteger(0);
Button button = (Button) b.getSource();
switch (button.getText()) {
case "开始":
new Thread(()->{VideoStrat();start();}).start();
services0 = new ScheduledThreadPoolExecutor(1);
services0.scheduleAtFixedRate(() -> {
try {
if (isstop) {
//System.out.println("thread1 = Thread.currentThread()");
return;
}
atomic.incrementAndGet();
String times = String.format("%02d:%02d:%02d", atomic.get() / 3600, atomic.get() % 3600 / 60,
atomic.get() % 60);
Platform.runLater(()->{
labels[1].setText(times);
Object timeout = OPTIONS.get("timeout");
if (timeout != null) {
int ints = (int)timeout;
slider.setProgress((atomic.get()*1.0)/(ints*1.0)*1.0);
if (atomic.get() == ints) {
stop();
flushall(filenames);
buttonss[0].setText("开始");
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}, 1, 1, TimeUnit.SECONDS);
button.setText("停止");
break;
case "暂停":
isstop = true;
button.setText("继续");
break;
case "继续":
isstop = false;
// if(thread1!=null)LockSupport.unpark(thread1);
// if(thread2!=null)LockSupport.unpark(thread2);
button.setText("暂停");
break;
case "停止":
stop();
labels[1].setText("00:00:00");
button.setText("开始");
flushall(filenames);
break;
}
});
return buttons;
}).toArray(Button[]::new);
// Label[] labels2 = Stream.of("关闭", "最小化").map(s -> {
// Label label = new Label(s);
// label.setStyle("-fx-font-size:18;-fx-text-fill:white");
// label.setCursor(Cursor.HAND);
// label.setOnMouseClicked(x -> {
// switch (((Label) x.getSource()).getText()) {
// case "关闭":
// primaryStage.close();
// break;
// case "最小化":
// primaryStage.setIconified(true);
// break;
// }
// });
// return label;
// }).toArray(Label[]::new);
ImageView[] images = Stream.of("12.png", "13.png", "zuixiao.png", "out.png").map(s -> {
ImageView imageView = new ImageView("file:///d:桌面/图标/" + s);
imageView.setId(s.split("\\.", 2)[0]);
imageView.setFitHeight(22);
imageView.setFitWidth(22);
imageView.setCursor(Cursor.HAND);
imageView.setOnMouseClicked(m -> {
switch (((ImageView) m.getSource()).getId()) {
case "zuixiao":
primaryStage.setIconified(true);
break;
case "out":
primaryStage.close();
break;
case "12":
stages.show();
break;
case "13":
ObservableList<Node> lists = borderPane.getChildren();
if (lists.contains(listview))
lists.remove(listview);
else
borderPane.setRight(listview);
break;
}
});
return imageView;
}).toArray(ImageView[]::new);
this.flowPanes = IntStream.range(0, 3).mapToObj(x -> {
FlowPane flowPane;
if (x < 1) {
flowPane = new FlowPane(Orientation.HORIZONTAL, 15, 0, labels[0], images[0], images[2], images[3]);
flowPane.setStyle("-fx-background-color:#0096ff;-fx-text-fill:white");
flowPane.setCursor(Cursor.MOVE);
flowPane.setPadding(new Insets(5));
flowPane.setPrefHeight(80);
flowPane.setAlignment(Pos.CENTER_LEFT);
} else if (x == 1) {
flowPane = new FlowPane(Orientation.HORIZONTAL, 20, 0, buttonss[0], buttonss[1], labels[1]);
flowPane.setPrefHeight(300);
flowPane.setAlignment(Pos.CENTER);
flowPane.setPadding(new Insets(20));
} else {
Label label = new Label("录制进度");
label.setStyle("-fx-font-size:20");
flowPane = new FlowPane(Orientation.HORIZONTAL, 30, 0, label, slider, images[1]);
flowPane.setStyle("-fx-background-color:white");
flowPane.setPrefHeight(80);
flowPane.setAlignment(Pos.CENTER);
flowPane.setPadding(new Insets(5));
}
FlowPane.setMargin(labels[0], new Insets(0,0,0,20));
FlowPane.setMargin(images[2], new Insets(0,0,0,360));
return flowPane;
}).toArray(FlowPane[]::new);
borderPane = new BorderPane(flowPanes[1]);
}
/**
* 获取视频的信息(包括时长,开始时间,结束时间等)
* @param vedio_paths
* @return
*/
private Map<String,Object> getVedioTime(String vedio_paths){
final Map<String,Object> time_map = new LinkedHashMap<String,Object>();
try {
final ProcessBuilder builder = new ProcessBuilder();
builder.redirectErrorStream(true);
builder.command("D:/桌面/FFmpeg/ffmpeg.exe","-i",vedio_paths);
final Process process = builder.start();
final StringWriter stringWriter = new StringWriter();
final DataInputStream datas = new DataInputStream(process.getInputStream());
for(String data="";(data = datas.readLine())!=null;stringWriter.write(data));
Matcher matchers = Pattern.compile("Duration: (\\d+?:\\d+?:\\d+?)\\.\\d+?, start: (.+?), bitrate: (\\d+?) kb/s").matcher(stringWriter.toString());
while(matchers.find()){
String vedio_length = matchers.group(1);
int vedio_second = LocalTime.parse(vedio_length).toSecondOfDay();
String vedio_time = matchers.group(2),vedio_bitrate = matchers.group(3);
time_map.put("duration",vedio_length);
time_map.put("second",vedio_second);
time_map.put("start",vedio_time);
time_map.put("bitrate",vedio_bitrate);
}
} catch (Exception e) {
e.printStackTrace();
}
return time_map;
}
/**
* 录屏结束之后的操作(转码,关机等,压缩,转GIF等功能)
* @param input_path 视频的保存位置
*/
private void flushall(String input_path){
Label label = new Label(Paths.get(input_path).getFileName().toString());
label.setStyle("-fx-font-size:16");
Label label2 = new Label("打开位置");
label2.setOnMouseClicked(x->{
try {
if(x.getButton()==MouseButton.PRIMARY)Desktop.getDesktop().open(new File(input_path).getParentFile());
} catch (IOException e) {
e.printStackTrace();
}
});
label2.setCursor(Cursor.HAND);
label2.setStyle("-fx-font-size:16");
HBox hBox2 = new HBox(20,label,label2);
hBox2.setPadding(new Insets(10));
hBox2.setAlignment(Pos.CENTER);
listview.getItems().add(hBox2);
int second = (int)getVedioTime(input_path).get("second");
String path = input_path.substring(0,input_path.lastIndexOf("\\")+1);
String name = path + input_path.substring(input_path.lastIndexOf("\\")+1,input_path.lastIndexOf("."))+"_new";
Map<String,String[]> maps = ImmutableMap
.of("iszhuan",String.format("D:/桌面/FFmpeg/ffmpeg.exe -i %s -vcodec h264 -q 0 -y %s",input_path,name+".mp4").split(" "),
"isgif",String.format("D:/桌面/FFmpeg/ffmpeg.exe -ss 0 -t %s -i %s -s %sx%s -r 15 %s",second,input_path,(int)rectangle.getWidth(),(int)rectangle.getHeight(),name+".gif").split(" "),
"isyasuo",String.format("D:/桌面/FFmpeg/ffmpeg.exe -threads 2 -i %s -b 200k %s",input_path,name+".mp4").split(" "),
"isshutdown","shutdown -s -t 10".split(" "));
String[] string = OPTIONS.keySet().stream().filter(x->(OPTIONS.get(x) instanceof Boolean) && ((Boolean)OPTIONS.get(x))==true).toArray(String[]::new);
try {
for (String string2 : string) {
if(Objects.equals("isplay",string2)){
Desktop.getDesktop().open(new File(input_path));
}else if(Objects.equals("isopen",string2)){
Desktop.getDesktop().open(new File(input_path).getParentFile());
}
String[] command = maps.get(string2);
if(command==null)continue;
switch(string2){
case "iszhuan":
alerts.setContentText("正在转码中");
break;
case "isgif":
alerts.setContentText("正在转GIF中");
break;
case "isyasuo":
alerts.setContentText("正在压缩中");
break;
case "isshutdown":
alerts.setContentText("正在关机中");
break;
}
alerts.show();
ProcessBuilder builder = new ProcessBuilder();
builder.redirectErrorStream(true);
builder.command(command);
Process process = builder.start();
new Thread(()->{
try {
byte[] bytes = new byte[1024];
InputStream inputStream = process.getInputStream();
for (int data = 0;(data = inputStream.read(bytes))>0;System.out.println(new String(bytes,0,data)));
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}).start();
process.waitFor();
alerts.close();
System.out.println("完成................................");
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void start(Stage primaryStage) throws Exception {
this.primaryStage = primaryStage;
Scene scene = new Scene(new VBox(0, flowPanes[0], borderPane, flowPanes[2]), 600, 300);
flowPanes[0].setOnMousePressed(event -> {
xOffSet = event.getSceneX();
yOffSet = event.getSceneY();
});
flowPanes[0].setOnMouseDragged(event -> {
primaryStage.setX(event.getScreenX() - xOffSet);
primaryStage.setY(event.getScreenY() - yOffSet);
});
// primaryStage.setAlwaysOnTop(true);
primaryStage.setFullScreenExitHint("");
primaryStage.setResizable(false);
primaryStage.initStyle(StageStyle.UNDECORATED);
primaryStage.setScene(scene);
primaryStage.show();
}
@SuppressWarnings("unchecked")
private void setEvent(ObservableList<Node> lists){
for (Node node : lists) {
if(node instanceof TextField){
TextField texts= (TextField)node;
boolean isnmber = texts.getText().matches("\\d+");
if(texts.getText().length()<1){
OPTIONS.remove(texts.getId());
}else if(Objects.equals(texts.getId(),"filename")){
OPTIONS.put(texts.getId(),texts.getText());
}else if(isnmber){
OPTIONS.put(texts.getId(),Integer.parseInt(texts.getText()));
}
}else if(node instanceof CheckBox){
CheckBox texts= (CheckBox)node;
OPTIONS.put(texts.getId(),texts.isSelected());
}else if(node instanceof ComboBox){
ComboBox<String> texts= (ComboBox<String>)node;
String values = texts.getValue();
if(values.matches("(\\d+)Hz"))values = values.split("H",2)[0];
if(values.matches("[a-z]+\\(.+\\)"))values = values.split("\\(",2)[0];
boolean isnmber = values.matches("\\d+");
System.out.println("values "+values);
OPTIONS.put(texts.getId(),isnmber?Integer.parseInt(values):values);
}
}
OPTIONS.forEach((a,b)->System.out.println(a+" : "+b));
}
/**
* 菜单UI面板
*
*/
private Stage getMenu() {
ObservableList<Node> lists = FXCollections.observableArrayList();
HBox[] hbox1 = Stream.of("字体大小", "字体颜色").map(x -> {
Label label = new Label(x);
label.setStyle("-fx-font-size:20");
TextField textField = new TextField();
textField.setStyle("-fx-font-size:20");
HBox hBox = new HBox(10, label, textField);
return hBox;
}).toArray(HBox[]::new);
RadioButton[] radio = Stream.of("全屏", "自定义").map(b -> {
RadioButton button = new RadioButton(b);
if (Objects.equals("全屏", b))
button.setSelected(true);
button.setToggleGroup(group);
button.setStyle("-fx-font-size:20");
button.setOnMouseClicked(m -> {
if(m.getButton()!=MouseButton.PRIMARY)return;
switch (((RadioButton) m.getSource()).getText()) {
case "全屏":
rectangle = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
System.out.println(rectangle);
break;
case "自定义":
show();
break;
}
});
return button;
}).toArray(RadioButton[]::new);
String[] message = {"是否录制音频", "录制完成关机", "录制完成播放", "录制完成压缩", "录制完成转码", "录制完成GIF", "录制完成打开"};
String[] ids = {"isaudio", "isshutdown", "isplay", "isyasuo", "iszhuan", "isgif", "isopen"};
CheckBox[] checks = IntStream.range(0,message.length).mapToObj(b -> {
CheckBox checkBox = new CheckBox(message[b]);
checkBox.setId(ids[b]);
lists.add(checkBox);
checkBox.setStyle("-fx-font-size:18");
return checkBox;
}).toArray(CheckBox[]::new);
// String[] strings = LongStream.iterate(1000000, x -> x + 1000000).limit(5).mapToObj(String::valueOf)
// .toArray(String[]::new);
// String[] string2 = new String[strings.length + 1];
// string2[0] = "视频比特率";
// System.arraycopy(strings, 0, string2, 1, strings.length);
String[] ids2 = {"bitrate","preset","channel","samplerate"};
@SuppressWarnings("unchecked")
ComboBox<String>[] comboBoxs = IntStream.range(0,boxtxt1.length).mapToObj(b -> {
ComboBox<String> comboBox = new ComboBox<String>(FXCollections.observableArrayList(boxtxt1[b]));
comboBox.setId(ids2[b]);
lists.add(comboBox);
comboBox.setValue(boxtxt1[b][0]);
comboBox.setStyle("-fx-font-size:18");
return comboBox;
}).toArray(ComboBox[]::new);
String[] ids3 = {"filename","nmber","timeout","frameRate"};
String[] message2 = { "视频名称", "视频质量", "定时时长", "录制速率", "录屏区域", "", "", "", "", "" , ""};
HBox[] hbox2 = IntStream.range(0, message2.length).mapToObj(x -> {
HBox hBox = null;
Label label = new Label(message2[x]);
label.setStyle("-fx-font-size:18");
if (x < 4) {
Label label2 = new Label("提示");
label2.setStyle("-fx-font-size:20;-fx-text-fill:white");
TextField save = new TextField();
save.setId(ids3[x]);
lists.add(save);
save.setStyle("-fx-font-size:18");
save.setPromptText("请输入" + message2[x]);
if (x == 2)
save.textProperty().addListener((a, b, c) -> {
if (c.matches("\\d+")) {
Integer nmber = Integer.valueOf(c);
label2.setText(
String.format("%02d:%02d:%02d", nmber / 3600, nmber % 3600 / 60, nmber % 60));
}
});
hBox = new HBox(20, label, save, label2);
} else if (x == 4) {
hBox = new HBox(20, label, radio[0], radio[1]);
} else if (x == 5) {
hBox = new HBox(20, label, checks[0], checks[1]);
} else if (x == 6) {
hBox = new HBox(20, label, checks[2], checks[3]);
} else if (x == 7) {
hBox = new HBox(20, label, checks[4], checks[5]);
} else if (x == 8) {
hBox = new HBox(20, label, checks[6]);
} else if (x == 9) {
hBox = new HBox(20, label, comboBoxs[0], comboBoxs[1]);
} else if (x == 10) {
hBox = new HBox(20, label, comboBoxs[2], comboBoxs[3]);
}
return hBox;
}).toArray(HBox[]::new);
Tab[] tabs = Stream.of("基本设置", "文件设置", "定时设置", "字体设置", "快捷设置").map(x -> {
Tab tab = new Tab(x);
if (Objects.equals(x, "字体设置")) {
VBox vBox = new VBox(10, hbox1);
vBox.setPadding(new Insets(20));
vBox.setAlignment(Pos.TOP_CENTER);
tab = new Tab(x, vBox);
} else if (Objects.equals(x, "基本设置")) {
VBox vBox = new VBox(10, hbox2);
vBox.setPadding(new Insets(10));
vBox.setAlignment(Pos.TOP_CENTER);
vBox.setStyle("-fx-background-color:#ddd");
ScrollPane scrollPane = new ScrollPane(vBox);
tab = new Tab(x, scrollPane);
} else if (Objects.equals(x, "文件设置")) {
Label label = new Label("录屏保存位置");
label.setStyle("-fx-font-size:18");
Button button = new Button("选择位置");
button.setPrefWidth(100);
button.setStyle(
"-fx-font-weight:bold;-fx-background-color:#0096ff;-fx-text-fill:white;-fx-font-size:18");
button.setOnAction(m -> save.setText(fileChooser.showDialog(primaryStage).getPath()));
HBox hBox = new HBox(10, label, save, button);
hBox.setPadding(new Insets(20));
hBox.setAlignment(Pos.TOP_CENTER);
tab = new Tab(x, hBox);
}
tab.setClosable(false);
tab.setStyle("-fx-font-size:18;-fx-background-color:white");
return tab;
}).toArray(Tab[]::new);
Stage stage = new Stage(StageStyle.UTILITY);
TabPane tabPane = new TabPane(tabs);
tabPane.setStyle("-fx-background-color:#ddd");
tabPane.setPrefHeight(300);
Button[] buttons = Stream.of("确定", "取消", "应用").map(x -> {
Button button = new Button(x);
button.setStyle("-fx-font-weight:bold;-fx-background-color:#0096ff;-fx-text-fill:white;-fx-font-size:18");
button.setOnAction(m -> {
Button button2 = (Button) m.getSource();
switch (button2.getText()) {
case "确定":
setEvent(lists);
stage.close();
break;
case "应用":
setEvent(lists);
break;
case "取消":
stage.close();
break;
}
});
return button;
}).toArray(Button[]::new);
HBox hBox = new HBox(10, buttons);
hBox.setAlignment(Pos.CENTER_RIGHT);
VBox vBox = new VBox(10, tabPane, hBox);
vBox.setPadding(new Insets(10));
vBox.setAlignment(Pos.TOP_CENTER);
stage.setResizable(false);
stage.setScene(new Scene(vBox, 500, 350));
return stage;
}
public void show() {
// 将主舞台缩放到任务栏
primaryStage.setIconified(true);
// 创建辅助舞台,并设置场景与布局
Stage stage = new Stage();
stage.initOwner(primaryStage);
HBox hBox = new HBox();
// 锚点布局采用半透明
AnchorPane anchorPane = new AnchorPane();
anchorPane.setStyle("-fx-background-color: #85858522");
// 场景设置白色全透明
Scene scene = new Scene(anchorPane);
scene.setFill(Paint.valueOf("#ffffff00"));
stage.setScene(scene);
// 清楚全屏中间提示文字
stage.setFullScreenExitHint("");
stage.initStyle(StageStyle.TRANSPARENT);
stage.setFullScreen(true);
stage.show();
// 切图窗口绑定鼠标按下事件
anchorPane.setOnMousePressed(event -> {
// 清除锚点布局中所有子元素
anchorPane.getChildren().clear();
// 设置背景保证能看到切图区域桌面
hBox.setBackground(null);
// 设置边框
hBox.setBorder(new Border(
new BorderStroke(Paint.valueOf("#c03700"), BorderStrokeStyle.SOLID, null, new BorderWidths(3))));
anchorPane.getChildren().add(hBox);
// 记录并设置起始位置
start_x = event.getSceneX();
start_y = event.getSceneY();
AnchorPane.setLeftAnchor(hBox, start_x);
AnchorPane.setTopAnchor(hBox, start_y);
});
// 绑定鼠标按下拖拽的事件
anchorPane.setOnMouseDragged(event -> {
// 用label记录切图区域的长宽
Label label = new Label();
label.setAlignment(Pos.CENTER);
label.setPrefHeight(30);
label.setPrefWidth(170);
anchorPane.getChildren().add(label);
AnchorPane.setLeftAnchor(label, start_x + 30);
AnchorPane.setTopAnchor(label, start_y);
label.setTextFill(Paint.valueOf("#ffffff"));// 白色填充
label.setStyle("-fx-background-color: #000000");// 黑背景
// 计算宽高并且完成切图区域的动态效果
w = Math.abs(event.getSceneX() - start_x);
h = Math.abs(event.getSceneY() - start_y);
hBox.setPrefWidth(w);
hBox.setPrefHeight(h);
label.setText("宽:" + w + " 高:" + h);
});
// 绑定鼠标松开事件
anchorPane.setOnMouseReleased(event -> {
// 记录最终长宽
w = Math.abs(event.getSceneX() - start_x);
h = Math.abs(event.getSceneY() - start_y);
anchorPane.setStyle("-fx-background-color: #00000000");
// 添加剪切按钮,并显示在切图区域的底部
Button b = new Button("确认选择");
hBox.setBorder(new Border(
new BorderStroke(Paint.valueOf("#85858544"), BorderStrokeStyle.SOLID, null, new BorderWidths(3))));
hBox.getChildren().add(b);
hBox.setAlignment(Pos.BOTTOM_RIGHT);
// 为切图按钮绑定切图事件
b.setOnAction(event1 -> {
rectangle = new Rectangle((int) start_x, (int) start_y, (int) w, (int) h);
System.out.println(rectangle);
// 切图辅助舞台小时
stage.close();
// 主舞台还原
primaryStage.setIconified(false);
});
});
scene.setOnKeyPressed(event -> {
if (event.getCode() == KeyCode.ESCAPE) {
stage.close();
primaryStage.setIconified(false);
}
});
}
public void VideoStrat() {
try {
int samplerate = (int) OPTIONS.get("samplerate");
int nmber = (int) OPTIONS.get("nmber");
int bitrate = (int) OPTIONS.get("bitrate");
int frameRate = (int) OPTIONS.get("frameRate");
int channel = (int) OPTIONS.get("channel");
String preset = (String) OPTIONS.get("preset");
Object filenames = OPTIONS.get("filename");
System.err.println((int) rectangle.getWidth()+" : "+(int) rectangle.getHeight());
if (filenames == null || ((String)filenames).length() < 1) {
filenames = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSS").format(LocalDateTime.now()) + ".mp4";
} else
filenames = String.format("%s.mp4", filenames);
System.err.println("filenames "+filenames);
recorder = new FFmpegFrameRecorder(this.filenames = (save.getText() + File.separator + filenames), (int) rectangle.getWidth(),
(int) rectangle.getHeight());
recorder.setVideoCodec(avcodec.AV_CODEC_ID_MPEG4);
recorder.setFormat("mp4");
recorder.setSampleRate(samplerate);
recorder.setFrameRate(frameRate);
recorder.setPixelFormat(0);
recorder.setVideoQuality(nmber);
recorder.setVideoOption("crf", "0");
recorder.setVideoBitrate(bitrate);
recorder.setVideoOption("preset",preset);
recorder.setAudioChannels(channel);
recorder.setAudioOption("crf", "0");
recorder.setAudioQuality(nmber);
recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
robot = new Robot();
} catch (AWTException e) {
System.out.println("Robot异常" + e.getMessage());
}
try {
recorder.start();
} catch (Exception e) {
System.out.println(" recorder.start()异常" + e.getMessage());
}
}
/**
* 开始录制
*/
public void start() {
if (startTime == 0)
startTime = System.currentTimeMillis();
if (pauseTime == 0)
pauseTime = System.currentTimeMillis();
boolean isaudio = (boolean) OPTIONS.get("isaudio");
int frameRate = (int) OPTIONS.get("frameRate");
if (isaudio) {
try {
audioFormat = new AudioFormat(44100.0F, 16, 2, true, false);
dataLineInfo = new DataLine.Info(TargetDataLine.class, audioFormat);
line = (TargetDataLine) AudioSystem.getLine(dataLineInfo);
line.open(audioFormat);
line.start();
} catch (LineUnavailableException e1) {
e1.printStackTrace();
}
sampleRate = (int) audioFormat.getSampleRate();
numChannels = audioFormat.getChannels();
int audioBufferSize = sampleRate * numChannels;
audioBytes = new byte[audioBufferSize];
}
services2 = new ScheduledThreadPoolExecutor(1);
services2.scheduleAtFixedRate(() -> {
if (isstop) {
//System.out.println("thread2 = Thread.currentThread()");
return;
}
BufferedImage screenCapture = robot.createScreenCapture(rectangle); // 截屏
BufferedImage videoImg = new BufferedImage((int)rectangle.getWidth(),(int)rectangle.getHeight(),
BufferedImage.TYPE_3BYTE_BGR); // 声明一个BufferedImage用重绘截图
Graphics2D videoGraphics = videoImg.createGraphics();// 创建videoImg的Graphics2D
videoGraphics.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
videoGraphics.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
videoGraphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
videoGraphics.drawImage(screenCapture,0,0,null); // 重绘截图
ShortBuffer sBuff = null;
if (isaudio) {
int nBytesRead = line.read(audioBytes, 0, line.available());
int nSamplesRead = nBytesRead / 2;
short[] samples = new short[nSamplesRead];
ByteBuffer.wrap(audioBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(samples);
sBuff = ShortBuffer.wrap(samples, 0, nSamplesRead);
}
try {
videoTS = 1000L * (System.currentTimeMillis() - startTime - (System.currentTimeMillis() - pauseTime));
if (videoTS > recorder.getTimestamp())
recorder.setTimestamp(videoTS);
recorder.record(java2dConverter.convert(videoImg));
if (isaudio)
recorder.recordSamples(sampleRate, numChannels, sBuff);
} catch (Exception e) {
e.printStackTrace();
}
// 释放资源
videoGraphics.dispose();
videoGraphics = null;
videoImg.flush();
videoImg = null;
screenCapture.flush();
screenCapture = null;
}, (int) (1000 / frameRate), (int) (1000 / frameRate), TimeUnit.MILLISECONDS);
}
/**
* 停止
*/
public void stop() {
try {
if (null != services2) {
services2.shutdownNow();
services2 = null;
recorder.close();
}
if (null != services0) {
services0.shutdownNow();
services0 = null;
}
if (null != line) {
line.stop();
line.close();
}
dataLineInfo = null;
audioFormat = null;
} catch (Throwable e) {
System.out.println(e.getMessage());
}
}
public static void main(String[] args) {
launch(args);
}
}