使用 JAVA 手写一个录屏GUI程序

7 篇文章 0 订阅
3 篇文章 0 订阅

使用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);
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用JavaCv实现录屏功能,需要使用FFmpeg进行视频编码,以下是一个简单的实现示例: 首先,需要引入JavaCv和FFmpeg的库文件,例如: ```xml <dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv</artifactId> <version>1.5.3</version> </dependency> <dependency> <groupId>org.bytedeco</groupId> <artifactId>ffmpeg</artifactId> <version>4.4</version> </dependency> ``` 然后,创建一个FFmpegFrameRecorder对象,设置视频编码器、视频格式、视频帧率等参数: ```java FFmpegFrameRecorder recorder = new FFmpegFrameRecorder("output.mp4", width, height); recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); recorder.setFormat("mp4"); recorder.setFrameRate(fps); recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P); recorder.start(); ``` 接着,使用Java.awt.Robot类进行屏幕捕获,获取屏幕上的图像: ```java Robot robot = new Robot(); Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()); BufferedImage image = robot.createScreenCapture(screenRect); ``` 将图像转换为JavaCv的Frame对象,写入到视频文件中: ```java Frame frame = converter.convert(image); recorder.record(frame); ``` 最后,当录屏完成时,需要停止录制并释放资源: ```java recorder.stop(); recorder.release(); ``` 完整的代码示例如下: ```java import org.bytedeco.ffmpeg.global.avcodec; import org.bytedeco.ffmpeg.global.avutil; import org.bytedeco.javacv.FFmpegFrameRecorder; import org.bytedeco.javacv.Frame; import org.bytedeco.javacv.Java2DFrameConverter; import java.awt.*; import java.awt.image.BufferedImage; public class ScreenRecorder { public static void main(String[] args) throws Exception { int width = 1920; int height = 1080; int fps = 30; FFmpegFrameRecorder recorder = new FFmpegFrameRecorder("output.mp4", width, height); recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); recorder.setFormat("mp4"); recorder.setFrameRate(fps); recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P); recorder.start(); Java2DFrameConverter converter = new Java2DFrameConverter(); Robot robot = new Robot(); Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()); long startTime = System.currentTimeMillis(); while (System.currentTimeMillis() - startTime < 60 * 1000) { BufferedImage image = robot.createScreenCapture(screenRect); Frame frame = converter.convert(image); recorder.record(frame); } recorder.stop(); recorder.release(); } } ``` 注意,由于录制屏幕需要操作系统的权限,所以需要以管理员身份运行程序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值