a new windowing system!

add:
+ CameraPanel ; To make life easier for coders
+ SwingIFrame ; Which is now our main UI component
+ VideoRecorder ; A helper Class for SwingIFrame to record cameras!

changed:
/ SwingCCTVManager ; changes for the new UI Component

deprecation:
/-/ AutoGainProcessor ; cause it isnt needed anymore, back then this was needed cause we opened the webcams manually (color wise)

---
rattatwinko
This commit is contained in:
2026-01-13 20:52:32 +01:00
parent b767ba27b3
commit ccc3d264f7
5 changed files with 698 additions and 27 deletions

View File

@@ -31,22 +31,37 @@ public class SwingCCTVManager {
private final JFrame frame;
private final JTable deviceTable;
private final DefaultTableModel tableModel;
private final SwingIFrame IFrame;
public SwingCCTVManager() {
frame = new JFrame("dashboard");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(1000, 600);
this.IFrame = new SwingIFrame();
this.IFrame.show();
String[] columns = {"Status", "Device Name", "Type", "Resolution", "Address"};
tableModel = new DefaultTableModel(columns, 0) {
@Override public boolean isCellEditable(int r, int c) { return false; }
@Override
public boolean isCellEditable(int r, int c) {
return false;
}
};
deviceTable = new JTable(tableModel);
deviceTable.setRowSelectionAllowed(true);
deviceTable.setFocusable(true);
deviceTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
setupTableAppearance();
deviceTable.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
deviceTable.requestFocusInWindow();
if (e.getClickCount() == 2 && deviceTable.getSelectedRow() != -1) {
launchSelected();
}
@@ -74,29 +89,31 @@ public class SwingCCTVManager {
}
private void setupTableAppearance() {
deviceTable.getColumnModel().getColumn(0).setMaxWidth(80); // Status column
deviceTable.getColumnModel().getColumn(0).setMaxWidth(80);
deviceTable.setRowHeight(30);
// Custom Renderer for Status Colors
deviceTable.getColumnModel().getColumn(0).setCellRenderer(new DefaultTableCellRenderer() {
@Override
public Component getTableCellRendererComponent(JTable t, Object v, boolean s, boolean f, int r, int c) {
Component comp = super.getTableCellRendererComponent(t, v, s, f, r, c);
if ("ONLINE".equals(v)) comp.setForeground(new Color(0, 150, 0));
else comp.setForeground(Color.RED);
setHorizontalAlignment(JLabel.CENTER);
return comp;
}
});
deviceTable.getColumnModel().getColumn(0)
.setCellRenderer(new DefaultTableCellRenderer() {
@Override
public Component getTableCellRendererComponent(
JTable t, Object v, boolean s, boolean f, int r, int c) {
Component comp = super.getTableCellRendererComponent(t, v, s, f, r, c);
setHorizontalAlignment(JLabel.CENTER);
comp.setForeground("ONLINE".equals(v)
? new Color(0, 150, 0)
: Color.RED);
return comp;
}
});
}
private void startAutoRefresh() {
Timer autoRefreshTimer = new Timer(5000, e -> refreshTable());
autoRefreshTimer.start();
new Timer(5000, e -> refreshTable()).start();
}
private void refreshTable() {
int selectedRow = deviceTable.getSelectedRow();
int[] selectedRows = deviceTable.getSelectedRows();
tableModel.setRowCount(0);
List<Webcam> webcams = Webcam.getWebcams();
@@ -114,14 +131,18 @@ public class SwingCCTVManager {
});
}
if (selectedRow != -1 && selectedRow < tableModel.getRowCount()) {
deviceTable.setRowSelectionInterval(selectedRow, selectedRow);
for (int r : selectedRows) {
if (r < tableModel.getRowCount()) {
deviceTable.addRowSelectionInterval(r, r);
}
}
}
private void showContextMenu(MouseEvent e) {
int row = deviceTable.rowAtPoint(e.getPoint());
deviceTable.setRowSelectionInterval(row, row);
if (row >= 0) {
deviceTable.setRowSelectionInterval(row, row);
}
JPopupMenu menu = new JPopupMenu();
JMenuItem launch = new JMenuItem("Launch Live Stream");
@@ -143,12 +164,16 @@ public class SwingCCTVManager {
String name = (String) tableModel.getValueAt(row, 1);
Webcam.getWebcams().stream()
.filter(w -> w.getName().equals(name))
.findFirst().ifPresent(selected -> new Thread(() -> new SwingCameraWindow(selected).open()).start());
.findFirst()
.ifPresent(cam ->
new Thread(() -> IFrame.addCameraInternalFrame(cam)).start()
);
}
private void deleteSelected() {
int row = deviceTable.getSelectedRow();
if (row == -1) return;
String name = (String) tableModel.getValueAt(row, 1);
if (name.toLowerCase().contains("usb")) return;
@@ -160,11 +185,15 @@ public class SwingCCTVManager {
private static void loadSavedCameras() {
for (CameraConfig config : CameraSettings.load()) {
try {
IpCamDeviceRegistry.register(config.getName(), config.getUrl(), IpCamMode.PUSH);
IpCamDeviceRegistry.register(
config.getName(),
config.getUrl(),
IpCamMode.PUSH
);
} catch (MalformedURLException e) {
JOptionPane.showMessageDialog(
null,
"Malformed URL\n"+e.getMessage(),
"Malformed URL\n" + e.getMessage(),
"Malformed URL",
JOptionPane.ERROR_MESSAGE
);
@@ -176,10 +205,16 @@ public class SwingCCTVManager {
JPanel p = new JPanel(new GridLayout(2, 2, 5, 5));
JTextField n = new JTextField();
JTextField u = new JTextField();
p.add(new JLabel("Name:")); p.add(n);
p.add(new JLabel("URL:")); p.add(u);
int result = JOptionPane.showConfirmDialog(frame, p, "Register IP Camera", JOptionPane.OK_CANCEL_OPTION);
p.add(new JLabel("Name:"));
p.add(n);
p.add(new JLabel("URL:"));
p.add(u);
int result = JOptionPane.showConfirmDialog(
frame, p, "Register IP Camera", JOptionPane.OK_CANCEL_OPTION
);
if (result == JOptionPane.OK_OPTION) {
try {
IpCamDeviceRegistry.register(n.getText(), u.getText(), IpCamMode.PUSH);

View File

@@ -0,0 +1,311 @@
package io.swtc;
import com.github.sarxos.webcam.Webcam;
import io.swtc.proccessing.WebcamCaptureLoop;
import io.swtc.proccessing.CameraPanel;
import io.swtc.recording.VideoRecorder;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
/*
*
* This file is basically just UI, its boring the interesting stuff is in the utilities section!
*
* */
public class SwingIFrame {
private final JFrame mainFrame;
private final JDesktopPane desktopPane;
public SwingIFrame() {
mainFrame = new JFrame("viewer");
mainFrame.setSize(1280,720);
mainFrame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
desktopPane = new JDesktopPane();
desktopPane.setBackground(Color.WHITE);
mainFrame.add(desktopPane, BorderLayout.CENTER);
}
public void addCameraInternalFrame(Webcam webcam) {
JInternalFrame iframe = new JInternalFrame(
webcam.getName(),
true, true, true, true
);
CameraPanel cameraPanel = new CameraPanel();
WebcamCaptureLoop captureLoop = new WebcamCaptureLoop(webcam, (BufferedImage img) ->
SwingUtilities.invokeLater(() -> cameraPanel.setImage(img))
);
JPanel contentPanel = new JPanel(new BorderLayout());
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.addTab("View", cameraPanel);
tabbedPane.addTab("Capture", createCapturePanel(cameraPanel, webcam));
tabbedPane.addTab("Effects", createEffectsPanel(cameraPanel));
contentPanel.add(tabbedPane, BorderLayout.CENTER);
iframe.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter() {
@Override
public void internalFrameClosing(javax.swing.event.InternalFrameEvent e) {
// if we dont call this the camera stays open until the procces dies.
captureLoop.stop();
}
});
iframe.add(contentPanel);
iframe.setSize(600, 500);
int offset = desktopPane.getAllFrames().length * 30;
iframe.setLocation(offset, offset);
desktopPane.add(iframe);
iframe.setVisible(true);
captureLoop.start();
}
private JPanel createCapturePanel(CameraPanel cameraPanel, Webcam webcam) {
JPanel panel = new JPanel(new BorderLayout(10, 10));
panel.setBorder(new EmptyBorder(15, 15, 15, 15));
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
// Screenshot section
JPanel screenshotPanel = new JPanel();
screenshotPanel.setLayout(new BoxLayout(screenshotPanel, BoxLayout.Y_AXIS));
screenshotPanel.setBorder(BorderFactory.createTitledBorder(
BorderFactory.createEtchedBorder(), "Screenshot",
TitledBorder.LEFT, TitledBorder.TOP));
JPanel screenshotPathPanel = new JPanel(new BorderLayout(5, 5));
JTextField screenshotPath = new JTextField(System.getProperty("user.home") + File.separator + "screenshots");
JButton browseSS = new JButton("...");
browseSS.setPreferredSize(new Dimension(40, 25));
browseSS.addActionListener(e -> browseDirectory(screenshotPath, panel));
screenshotPathPanel.add(screenshotPath, BorderLayout.CENTER);
screenshotPathPanel.add(browseSS, BorderLayout.EAST);
JButton takeScreenshot = new JButton("Take Screenshot (S)");
takeScreenshot.setAlignmentX(Component.CENTER_ALIGNMENT);
takeScreenshot.addActionListener(e -> saveSnapshot(cameraPanel, webcam, screenshotPath.getText(), panel));
screenshotPanel.add(screenshotPathPanel);
screenshotPanel.add(Box.createRigidArea(new Dimension(0, 10)));
screenshotPanel.add(takeScreenshot);
// Recording section
JPanel recordPanel = new JPanel();
recordPanel.setLayout(new BoxLayout(recordPanel, BoxLayout.Y_AXIS));
recordPanel.setBorder(BorderFactory.createTitledBorder(
BorderFactory.createEtchedBorder(), "Recording",
TitledBorder.LEFT, TitledBorder.TOP));
JPanel recordPathPanel = new JPanel(new BorderLayout(5, 5));
JTextField recordPath = new JTextField(System.getProperty("user.home") + File.separator + "recordings");
JButton browseRec = new JButton("...");
browseRec.setPreferredSize(new Dimension(40, 25));
browseRec.addActionListener(e -> browseDirectory(recordPath, panel));
recordPathPanel.add(recordPath, BorderLayout.CENTER);
recordPathPanel.add(browseRec, BorderLayout.EAST);
VideoRecorder recorder = new VideoRecorder();
JButton recordButton = new JButton("Start Recording (R)");
JLabel recordingStatus = new JLabel("Ready");
recordingStatus.setAlignmentX(Component.CENTER_ALIGNMENT);
recordButton.setAlignmentX(Component.CENTER_ALIGNMENT);
recordButton.addActionListener(e -> {
if (!recorder.isRecording()) {
String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String filename = "recording_" + webcam.getName().replaceAll("[^a-zA-Z0-9]", "_")
+ "_" + timestamp + ".mp4";
File dir = new File(recordPath.getText());
dir.mkdirs();
try {
recorder.startRecording(cameraPanel, new File(dir, filename));
recordButton.setText("Stop Recording");
recordingStatus.setText("Recording...");
recordingStatus.setForeground(Color.RED);
} catch (Exception ex) {
JOptionPane.showMessageDialog(panel,
"Error starting recording: " + ex.getMessage(),
"Error", JOptionPane.ERROR_MESSAGE);
}
} else {
try {
File saved = recorder.stopRecording();
recordButton.setText("Start Recording (R)");
recordingStatus.setText("Saved: " + saved.getName());
recordingStatus.setForeground(Color.BLACK);
} catch (Exception ex) {
recordButton.setText("Start Recording (R)");
recordingStatus.setText("Error saving");
recordingStatus.setForeground(Color.RED);
JOptionPane.showMessageDialog(panel,
"Error saving recording: " + ex.getMessage(),
"Error", JOptionPane.ERROR_MESSAGE);
}
}
});
recordPanel.add(recordPathPanel);
recordPanel.add(Box.createRigidArea(new Dimension(0, 10)));
recordPanel.add(recordButton);
recordPanel.add(Box.createRigidArea(new Dimension(0, 5)));
recordPanel.add(recordingStatus);
mainPanel.add(screenshotPanel);
mainPanel.add(Box.createRigidArea(new Dimension(0, 15)));
mainPanel.add(recordPanel);
mainPanel.add(Box.createVerticalGlue());
panel.add(mainPanel, BorderLayout.NORTH);
return panel;
}
private JPanel createEffectsPanel(CameraPanel cameraPanel) {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBorder(new EmptyBorder(15, 15, 15, 15));
JPanel transformPanel = new JPanel(new GridLayout(0, 1, 5, 5));
transformPanel.setBorder(BorderFactory.createTitledBorder(
BorderFactory.createEtchedBorder(), "Transform",
TitledBorder.LEFT, TitledBorder.TOP));
JCheckBox mirrorCheck = new JCheckBox("Mirror Horizontal");
JCheckBox flipCheck = new JCheckBox("Flip Vertical");
JCheckBox rotateCheck = new JCheckBox("Rotate 180°");
mirrorCheck.addActionListener(e -> cameraPanel.setMirror(mirrorCheck.isSelected()));
flipCheck.addActionListener(e -> cameraPanel.setFlip(flipCheck.isSelected()));
rotateCheck.addActionListener(e -> cameraPanel.setRotate(rotateCheck.isSelected()));
transformPanel.add(mirrorCheck);
transformPanel.add(flipCheck);
transformPanel.add(rotateCheck);
JPanel colorPanel = new JPanel();
colorPanel.setLayout(new BoxLayout(colorPanel, BoxLayout.Y_AXIS));
colorPanel.setBorder(BorderFactory.createTitledBorder(
BorderFactory.createEtchedBorder(), "Color",
TitledBorder.LEFT, TitledBorder.TOP));
JCheckBox grayscaleCheck = new JCheckBox("Grayscale");
JCheckBox invertCheck = new JCheckBox("Invert Colors");
grayscaleCheck.addActionListener(e -> cameraPanel.setGrayscale(grayscaleCheck.isSelected()));
invertCheck.addActionListener(e -> cameraPanel.setInvert(invertCheck.isSelected()));
colorPanel.add(grayscaleCheck);
colorPanel.add(Box.createRigidArea(new Dimension(0, 5)));
colorPanel.add(invertCheck);
JPanel brightnessPanel = new JPanel();
brightnessPanel.setLayout(new BoxLayout(brightnessPanel, BoxLayout.Y_AXIS));
brightnessPanel.setBorder(BorderFactory.createTitledBorder(
BorderFactory.createEtchedBorder(), "Adjustments",
TitledBorder.LEFT, TitledBorder.TOP));
JPanel brightnessSliderPanel = new JPanel(new BorderLayout());
JLabel brightnessLabel = new JLabel("Brightness: 0");
JSlider brightnessSlider = new JSlider(-100, 100, 0);
brightnessSlider.addChangeListener(e -> {
int value = brightnessSlider.getValue();
brightnessLabel.setText("Brightness: " + value);
cameraPanel.setBrightness(value);
});
brightnessSliderPanel.add(brightnessLabel, BorderLayout.NORTH);
brightnessSliderPanel.add(brightnessSlider, BorderLayout.CENTER);
JPanel contrastSliderPanel = new JPanel(new BorderLayout());
JLabel contrastLabel = new JLabel("Contrast: 1.0");
JSlider contrastSlider = new JSlider(0, 200, 100);
contrastSlider.addChangeListener(e -> {
float value = contrastSlider.getValue() / 100f;
contrastLabel.setText("Contrast: " + String.format("%.1f", value));
cameraPanel.setContrast(value);
});
contrastSliderPanel.add(contrastLabel, BorderLayout.NORTH);
contrastSliderPanel.add(contrastSlider, BorderLayout.CENTER);
brightnessPanel.add(brightnessSliderPanel);
brightnessPanel.add(Box.createRigidArea(new Dimension(0, 10)));
brightnessPanel.add(contrastSliderPanel);
JButton resetButton = new JButton("Reset All Effects");
resetButton.setAlignmentX(Component.CENTER_ALIGNMENT);
resetButton.addActionListener(e -> {
mirrorCheck.setSelected(false);
flipCheck.setSelected(false);
rotateCheck.setSelected(false);
grayscaleCheck.setSelected(false);
invertCheck.setSelected(false);
brightnessSlider.setValue(0);
contrastSlider.setValue(100);
cameraPanel.resetEffects();
});
panel.add(transformPanel);
panel.add(Box.createRigidArea(new Dimension(0, 10)));
panel.add(colorPanel);
panel.add(Box.createRigidArea(new Dimension(0, 10)));
panel.add(brightnessPanel);
panel.add(Box.createRigidArea(new Dimension(0, 15)));
panel.add(resetButton);
panel.add(Box.createVerticalGlue());
return panel;
}
private void browseDirectory(JTextField field, Component parent) {
JFileChooser chooser = new JFileChooser(field.getText());
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
if (chooser.showOpenDialog(parent) == JFileChooser.APPROVE_OPTION) {
field.setText(chooser.getSelectedFile().getAbsolutePath());
}
}
private void saveSnapshot(CameraPanel cameraPanel, Webcam webcam, String directory, Component parent) {
BufferedImage img = cameraPanel.getCurrentProcessedImage();
if (img != null) {
try {
File dir = new File(directory);
dir.mkdirs();
String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String filename = "screenshot_" + webcam.getName().replaceAll("[^a-zA-Z0-9]", "_")
+ "_" + timestamp + ".png";
File file = new File(dir, filename);
ImageIO.write(img, "PNG", file);
} catch (Exception ex) {
JOptionPane.showMessageDialog(parent,
"Error: " + ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
}
}
public void show() {
mainFrame.setLocationRelativeTo(null);
mainFrame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
SwingIFrame dashboard = new SwingIFrame();
dashboard.show();
});
}
}

View File

@@ -1,5 +1,12 @@
package io.swtc.proccessing;
/*
*
* Soon to be deprecated!
*
* */
@Deprecated
public class AutoGainProcessor {
public float[] calculateAutoGains(int[] pixels) {

View File

@@ -0,0 +1,204 @@
package io.swtc.proccessing;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
/*
*
* Now here its getting rather interesting! this class processes some
* important stuff!
*
* */
public class CameraPanel extends JPanel {
private BufferedImage currentImage;
private boolean mirror = false;
private boolean flip = false;
private boolean rotate = false;
private boolean grayscale = false;
private boolean invert = false;
private int brightness = 0;
private float contrast = 1.0f;
public void setImage(BufferedImage img) {
this.currentImage = img;
this.repaint();
}
public BufferedImage getCurrentImage() {
return currentImage;
}
public BufferedImage getCurrentProcessedImage() {
if (currentImage == null) {
return null;
}
BufferedImage processed = currentImage;
// apply color effects
if (grayscale || invert || brightness != 0 || contrast != 1.0f) {
processed = applyColorEffects(processed);
}
// apply transform.
if (mirror || flip || rotate) {
processed = applyTransforms(processed);
}
return processed;
}
/* Helper Methods ... its the same boilerplate shit over and over again, im sick of this. */
public void setMirror(boolean mirror) {
this.mirror = mirror;
this.repaint();
}
public void setFlip(boolean flip) {
this.flip = flip;
this.repaint();
}
public void setRotate(boolean rotate) {
this.rotate = rotate;
this.repaint();
}
public void setGrayscale(boolean grayscale) {
this.grayscale = grayscale;
this.repaint();
}
public void setInvert(boolean invert) {
this.invert = invert;
this.repaint();
}
public void setBrightness(int brightness) {
this.brightness = brightness;
this.repaint();
}
public void setContrast(float contrast) {
this.contrast = contrast;
this.repaint();
}
public void resetEffects() {
mirror = flip = rotate = grayscale = invert = false;
brightness = 0;
contrast = 1.0f;
this.repaint();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (currentImage != null) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
BufferedImage processedImage = currentImage;
// effects
if (grayscale || invert || brightness != 0 || contrast != 1.0f) {
processedImage = applyColorEffects(processedImage);
}
// transforms
int w = getWidth(), h = getHeight();
if (rotate) {
g2.translate(w / 2, h / 2);
g2.rotate(Math.PI);
g2.translate(-w / 2, -h / 2);
}
// here we have if, cause we need to do the stuff, yk?
if (mirror && flip) {
g2.drawImage(processedImage, w, h, -w, -h, null);
} else if (mirror) {
g2.drawImage(processedImage, w, 0, -w, h, null);
} else if (flip) {
g2.drawImage(processedImage, 0, h, w, -h, null);
} else {
g2.drawImage(processedImage, 0, 0, w, h, null);
}
}
}
private BufferedImage applyColorEffects(BufferedImage img) {
BufferedImage result = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB);
for (int y = 0; y < img.getHeight(); y++) {
for (int x = 0; x < img.getWidth(); x++) {
int rgb = img.getRGB(x, y);
int r = (rgb >> 16) & 0xFF;
int g = (rgb >> 8) & 0xFF;
int b = rgb & 0xFF;
if (grayscale) {
int gray = (r + g + b) / 3;
r = g = b = gray;
}
// this is fun, this regulates the brightness or the contrast!
// this is some real java, the other stuff is just UI design, which i am bad at,
// but this! This is some real shit
r = (int) ((r - 128) * contrast + 128 + brightness);
g = (int) ((g - 128) * contrast + 128 + brightness);
b = (int) ((b - 128) * contrast + 128 + brightness);
// invert the colors!
if (invert) {
r = 255 - r;
g = 255 - g;
b = 255 - b;
}
// clamp so we dont get into weird color grades, or any weird looking spaces
// if we dont do this we cant really do stuff which looks bearable
r = Math.max(0, Math.min(255, r));
g = Math.max(0, Math.min(255, g));
b = Math.max(0, Math.min(255, b));
result.setRGB(x, y, (r << 16) | (g << 8) | b);
}
}
return result;
}
private BufferedImage applyTransforms(BufferedImage img) {
int width = img.getWidth();
int height = img.getHeight();
BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int sourceX = x;
int sourceY = y;
if (mirror) {
sourceX = width - 1 - x;
}
if (flip) {
sourceY = height - 1 - y;
}
if (rotate) {
int tempX = width - 1 - sourceX;
int tempY = height - 1 - sourceY;
sourceX = tempX;
sourceY = tempY;
}
result.setRGB(x, y, img.getRGB(sourceX, sourceY));
}
}
return result;
}
}

View File

@@ -0,0 +1,114 @@
package io.swtc.recording;
import io.swtc.proccessing.CameraPanel;
import org.jcodec.api.awt.AWTSequenceEncoder;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.io.SeekableByteChannel;
import org.jcodec.common.model.Rational;
import javax.swing.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
/*
*
* This is not that interesting but surely improtant for any security based people,
* here we record using jcodec, which is more efficient than me writing my own recorder,
* which i am certainly not doing.
*
* Anyways we dont do shit properly anyway.
*
* */
public class VideoRecorder {
private boolean recording = false;
private Timer captureTimer;
private File outputFile;
private AWTSequenceEncoder encoder;
private SeekableByteChannel channel;
private static final int FPS = 30;
public void startRecording(CameraPanel panel, File output) throws IOException {
this.outputFile = output;
this.recording = true;
channel = NIOUtils.writableFileChannel(String.valueOf(outputFile));
encoder = new AWTSequenceEncoder(channel, Rational.R(FPS, 1));
captureTimer = new Timer(1000 / FPS, e -> {
try {
BufferedImage img = panel.getCurrentProcessedImage();
if (img != null) {
BufferedImage rgbImage = convertToRGB(img);
encoder.encodeImage(rgbImage);
}
} catch (IOException ex) {
JOptionPane.showMessageDialog(
null,
"IOException\n" + ex,
"IOException in Recorder",
JOptionPane.ERROR_MESSAGE
);
try {
stopRecording();
} catch (IOException stopEx) {
JOptionPane.showMessageDialog(
null,
"IOException@stopEx\n" + stopEx,
"IOException in Recorder@stopEx",
JOptionPane.ERROR_MESSAGE
);
}
}
});
captureTimer.start();
}
public File stopRecording() throws IOException {
recording = false;
/* some helper methods, i swear its always the same? */
if (captureTimer != null) {
captureTimer.stop();
}
if (encoder != null) {
encoder.finish();
}
if (channel != null) {
channel.close();
}
return outputFile;
}
public boolean isRecording() {
return recording;
}
// maybe i should move this to a component, some useless conversion right here,
// (performance wise)
// yes, capitain?
private BufferedImage convertToRGB(BufferedImage source) {
if (source.getType() == BufferedImage.TYPE_INT_RGB) {
return source;
}
BufferedImage rgb = new BufferedImage(
source.getWidth(),
source.getHeight(),
BufferedImage.TYPE_INT_RGB
);
for (int y = 0; y < source.getHeight(); y++) {
for (int x = 0; x < source.getWidth(); x++) {
rgb.setRGB(x, y, source.getRGB(x, y) & 0xFFFFFF);
}
}
return rgb;
}
}