У меня проблема с ОЗУ при рендеринге изображения с веб-камеры (с помощью sarxos-webcam) в потоке JavaFX, что приводит к сбою приложения. Используемый стек:
Java 8 / JavaFX 8
Sarxos webcam capture 0.3.12
Camera driver: OpenIMAJ 0.3.12 (currently the only tested driver which provide like 30 FPS on tablet with IMX175)
Windows 10
Когда я запускаю потоковую передачу с веб-камеры, потребность в памяти значительно увеличивается, например, с 400 МБ до более 2 ГБ ОЗУ и запуска / остановки камеры несколько раз, но когда камера затем выключается, и я меняю Сцена JavaFX, память не падает, поэтому приложение выходит из строя, например, через час (модуль камеры является лишь частью этого приложения).
Вот код, вызывающий проблему:
private void startCameraStream() {
Platform.runLater(() -> {
webcam = Webcam.getDefault();
if (!webcam.isOpen()) {
webcam.setViewSize(WebcamResolution.HD.getSize());
webcam.open(false);
}
Task<Void> task = getImageTask();
Thread th = new Thread(task);
th.start();
image.imageProperty().bind(imageProperty);
});
}
private Task<Void> getImageTask() {
return new Task<Void>() {
@Override
protected Void call() {
while (!pauseWebcam) {
try {
if ((grabbedImage = webcam.getImage()) != null) {
Platform.runLater(() -> {
Image mainImage = SwingFXUtils.toFXImage(grabbedImage, null);
imageProperty.set(mainImage);
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
grabbedImage.flush();
}
}
return null;
}
};
}
Как вы можете видеть, я пробовал методы типа "flu sh" внутри или вне потока JavaFX, но это не помогло. Потребность в памяти останавливается на каком-то уровне, но остальная часть приложения становится непригодной для использования.
Есть ли способ предотвратить или очистить память после изменения сцены?
Я также создал минимальный смысл Пример, показывающий проблему в рабочем приложении: https://gist.github.com/AreXe/2e5dd350ef4076126cc62156e0cb98fa
Отдельные файлы:
pom. xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com</groupId>
<artifactId>camera</artifactId>
<version>1.0</version>
<dependencies>
<dependency>
<groupId>com.github.sarxos</groupId>
<artifactId>webcam-capture</artifactId>
<version>0.3.12</version>
</dependency>
<dependency>
<groupId>com.github.sarxos</groupId>
<artifactId>webcam-capture-driver-openimaj</artifactId>
<version>0.3.12</version>
</dependency>
</dependencies>
</project>
src \ main \ java \ Main. java
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import java.io.IOException;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(createStartScene());
primaryStage.show();
}
public Scene createStartScene() throws IOException {
return new Scene(loadStartPane());
}
private Pane loadStartPane() throws IOException {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("camera.fxml"));
Pane startPane = loader.load();
loader.getController();
return startPane;
}
}
src \ main \ java \ CameraController. java
import com.github.sarxos.webcam.Webcam;
import com.github.sarxos.webcam.WebcamResolution;
import com.github.sarxos.webcam.ds.openimaj.OpenImajDriver;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.concurrent.Task;
import javafx.embed.swing.SwingFXUtils;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.StackPane;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.util.ResourceBundle;
public class CameraController implements Initializable {
static {
Webcam.setDriver(new OpenImajDriver());
}
@FXML
StackPane rootPane;
@FXML
ImageView image;
private Webcam webcam;
private ObjectProperty<Image> imageProperty = new SimpleObjectProperty<>();
private BufferedImage grabbedImage;
private static BooleanProperty stopWebcam = new SimpleBooleanProperty(false);
@Override
public void initialize(URL location, ResourceBundle resources) {
stopWebcam.setValue(false);
image.setFitWidth(rootPane.getWidth() - 20d);
setCameraStopListener();
}
@FXML
private void cameraStart() {
stopWebcam.setValue(false);
startCameraStream();
}
@FXML
private void cameraStop() {
stopWebcam.setValue(true);
}
private void startCameraStream() {
Platform.runLater(() -> {
webcam = Webcam.getDefault();
if (!webcam.isOpen()) {
webcam.setViewSize(WebcamResolution.HD.getSize());
webcam.open(false);
}
Task<Void> task = getImageTask();
Thread th = new Thread(task);
th.start();
image.imageProperty().bind(imageProperty);
});
}
private Task<Void> getImageTask() {
return new Task<Void>() {
@Override
protected Void call() {
while (!stopWebcam.get()) {
try {
if ((grabbedImage = webcam.getImage()) != null) {
Platform.runLater(() -> {
Image mainImage = SwingFXUtils.toFXImage(grabbedImage, null);
imageProperty.set(mainImage);
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
grabbedImage.flush();
}
}
return null;
}
};
}
private void setCameraStopListener() {
stopWebcam.addListener((obs, oldValue, newValue) -> {
if (newValue && webcam != null && webcam.isOpen()) {
webcam.close();
}
});
}
}
src \ main \ resources \ camera.f xml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.StackPane?>
<StackPane fx:id="rootPane" prefHeight="600.0" prefWidth="840.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="CameraController">
<children>
<ImageView fx:id="image" fitHeight="480.0" fitWidth="768.0" pickOnBounds="true" preserveRatio="true" StackPane.alignment="TOP_CENTER" />
<HBox alignment="BOTTOM_CENTER" prefHeight="100.0" prefWidth="200.0" StackPane.alignment="BOTTOM_CENTER">
<children>
<Button onAction="#cameraStart" mnemonicParsing="false" prefHeight="50.0" prefWidth="100.0" text="Start" />
<Button onAction="#cameraStop" mnemonicParsing="false" prefHeight="50.0" prefWidth="100.0" text="Stop" />
</children>
</HBox>
</children>
</StackPane>