I'm developing a screen recording application using java. I've modified code from https://dzone.com/articles/screen-record-play-using-java where all the external jars required for this application are available.
If I lower captureInterval
to 25 then the application runs out of memory - i.e. it throws a java.lang.OutOfMemoryError exception on the line below within the Recorder method startRecord()
.
List<Image> resolutionVariants = mrImage.getResolutionVariants();
How can the code be more efficient with memory?
Recorder.java
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.MultiResolutionImage;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.imageio.ImageIO;
import javax.media.MediaLocator;
public class Recorder {
/**
* Screen Width.
*/
public static final int screenWidth = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth();
/**
* Screen Height.
*/
public static final int screenHeight = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight();
/**
* Interval between which the image needs to be captured.
*/
public static final int captureInterval = 41;
/**
* Base folder to store the recorded video.
*/
public static final String videoPath = System.getProperty("user.home") + File.separator + "Desktop" + File.separator + "Records" + File.separator;
/**
* Temporary folder to store the screenshot.
*/
public static final String store = System.getProperty("user.home") + File.separator + "Desktop" + File.separator + "Records" + File.separator + "tmp" + File.separator;
/**
* Status of the recorder.
*/
private static AtomicBoolean record = new AtomicBoolean(false);
private static Thread recordThread;
private static Thread processThread;
private static BlockingDeque<Image> imageList = new LinkedBlockingDeque<>();
public static void startRecord() {
record.set(true);
recordThread = new Thread(() -> {
try {
Robot rt = new Robot();
Rectangle rect = new Rectangle(screenWidth, screenHeight);
while (record.get()) {
long startTime = System.currentTimeMillis();
Image nativeResImage;
MultiResolutionImage mrImage = rt.createMultiResolutionScreenCapture(rect);
List<Image> resolutionVariants = mrImage.getResolutionVariants();
if (resolutionVariants.size() > 1)
nativeResImage = resolutionVariants.get(1);
else
nativeResImage = resolutionVariants.get(0);
imageList.putLast(nativeResImage);
long endTime = System.currentTimeMillis();
long sleepTime = captureInterval - (endTime - startTime);
if (sleepTime > 0)
Thread.sleep(sleepTime);
}
} catch (Exception e) {
e.printStackTrace();
}
});
processThread = new Thread(() -> {
while (record.get() || (!imageList.isEmpty())) {
try {
Image nativeResImage = imageList.takeFirst();
ImageIO.write((BufferedImage) nativeResImage, "jpeg", new File(store
+ System.currentTimeMillis() + ".jpeg"));
} catch (InterruptedException | IOException e) {
e.printStackTrace();
}
}
});
processThread.start();
recordThread.start();
System.out.println("\nEasy Capture is recording now!!!!!!!");
}
}
Main.java
import java.awt.*;
import java.io.File;
import java.util.Scanner;
import com.bel.recorder.Recorder;
public class Main {
public static void main(String[] args) {
System.out.println("######### Starting Easy Capture Recorder #######");
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
System.out.println("Your Screen [Width, Height]: " + "[" + screen.getWidth() + ", " + screen.getHeight() + "]");
Scanner sc = new Scanner(System.in);
System.out.println("Rate [" + 24 + "] Frames/Per Sec.");
File fileDir = new File(Recorder.videoPath);
if (!fileDir.exists()) {
if (!fileDir.mkdir())
System.err.println("Error in creating the directory " + fileDir.getName());
}
fileDir = new File(Recorder.store);
if (!fileDir.exists()) {
if (!fileDir.mkdir())
System.err.println("Error in creating the directory " + fileDir.getName());
}
Recorder.startRecord();
System.out.println("Press x to exit:");
String exit = sc.next();
while (exit == null || "".equals(exit) || !"x".equalsIgnoreCase(exit)) {
System.out.println("\nPress x to exit:");
exit = sc.next();
}
// Recorder.stopRecord();
File tmpDirectory = new File(Recorder.store);
File[] files = tmpDirectory.listFiles();
if (files == null)
return;
for (File file : files)
file.delete();
tmpDirectory.delete();
}
}
recordThread
starts to produce frames faster thanprocessThread
may save them to a disk. \$\endgroup\$Integer.Max_value
. One thing I would do is create the deque with a fixed capacity with the appropriate constructor. \$\endgroup\$