This commit is contained in:
rattatwinko
2025-05-27 16:34:09 +02:00
commit cceb0d44e8
21 changed files with 811 additions and 0 deletions

View File

@@ -0,0 +1,142 @@
package com.jsca;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import javax.swing.*;
import java.io.*;
import java.util.*;
public class CameraManager {
private final Map<Integer, CameraPanel> cameras = new HashMap<>();
private final JPanel gridPanel;
private static final int MAX_CAMERAS = 4;
private static final String CONFIG_FILE = "camera_config.json";
private final Gson gson;
public CameraManager(JPanel gridPanel) {
this.gridPanel = gridPanel;
this.gson = new GsonBuilder().setPrettyPrinting().create();
// Initialize empty panels
for (int i = 0; i < MAX_CAMERAS; i++) {
CameraPanel emptyPanel = new CameraPanel("Empty " + (i + 1));
cameras.put(i, emptyPanel);
gridPanel.add(emptyPanel);
}
}
public boolean addCamera(String name, StreamReader streamReader, int position) {
if (position < 0 || position >= MAX_CAMERAS) {
return false;
}
CameraPanel oldPanel = cameras.get(position);
if (oldPanel != null) {
oldPanel.stopStream();
}
CameraPanel newPanel = new CameraPanel(name);
newPanel.startStream(streamReader);
cameras.put(position, newPanel);
gridPanel.remove(position);
gridPanel.add(newPanel, position);
gridPanel.revalidate();
gridPanel.repaint();
return true;
}
public void removeCamera(int position) {
if (position >= 0 && position < MAX_CAMERAS) {
CameraPanel panel = cameras.get(position);
if (panel != null) {
panel.stopStream();
CameraPanel emptyPanel = new CameraPanel("Empty " + (position + 1));
cameras.put(position, emptyPanel);
gridPanel.remove(position);
gridPanel.add(emptyPanel, position);
gridPanel.revalidate();
gridPanel.repaint();
}
}
}
public void stopAllCameras() {
for (CameraPanel panel : cameras.values()) {
if (panel != null) {
panel.stopStream();
}
}
}
public CameraPanel getCameraPanel(int position) {
return cameras.get(position);
}
public void restartCamera(int position) {
CameraPanel panel = cameras.get(position);
if (panel != null && panel.isStreaming()) {
StreamReader currentStream = panel.getCurrentStream();
if (currentStream != null) {
panel.stopStream();
panel.startStream(currentStream);
}
}
}
public void saveConfiguration() {
List<CameraConfig> configs = new ArrayList<>();
for (Map.Entry<Integer, CameraPanel> entry : cameras.entrySet()) {
CameraPanel panel = entry.getValue();
if (panel.isStreaming()) {
CameraConfig config = new CameraConfig();
config.position = entry.getKey();
config.name = panel.getCameraName();
// Add more configuration details as needed
configs.add(config);
}
}
try (Writer writer = new FileWriter(CONFIG_FILE)) {
gson.toJson(configs, writer);
} catch (IOException e) {
JOptionPane.showMessageDialog(null,
"Error saving configuration: " + e.getMessage(),
"Save Error",
JOptionPane.ERROR_MESSAGE);
}
}
public void loadConfiguration() {
try (Reader reader = new FileReader(CONFIG_FILE)) {
List<CameraConfig> configs = gson.fromJson(reader,
new TypeToken<List<CameraConfig>>(){}.getType());
if (configs != null) {
stopAllCameras();
for (CameraConfig config : configs) {
// Implement camera restoration based on config
// This is a placeholder for the actual implementation
// You'll need to create appropriate StreamReader instances
}
}
} catch (IOException e) {
JOptionPane.showMessageDialog(null,
"Error loading configuration: " + e.getMessage(),
"Load Error",
JOptionPane.ERROR_MESSAGE);
}
}
private static class CameraConfig {
int position;
String name;
String type; // "network" or "local"
String url; // for network cameras
String username;
String password;
int deviceIndex; // for local cameras
}
}

View File

@@ -0,0 +1,99 @@
package com.jsca;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import javax.imageio.ImageIO;
public class CameraPanel extends JPanel {
private final JLabel videoLabel;
private final String cameraName;
private volatile boolean isStreaming = false;
private StreamReader currentStream;
public CameraPanel(String cameraName) {
this.cameraName = cameraName;
setLayout(new BorderLayout());
// Create video display panel
videoLabel = new JLabel();
videoLabel.setPreferredSize(new Dimension(640, 480));
videoLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
add(videoLabel, BorderLayout.CENTER);
// Create control panel
JPanel controlPanel = new JPanel();
controlPanel.setLayout(new FlowLayout());
JButton snapshotButton = new JButton("Snapshot");
snapshotButton.addActionListener(e -> takeSnapshot());
controlPanel.add(snapshotButton);
add(controlPanel, BorderLayout.SOUTH);
}
public void startStream(StreamReader streamReader) {
stopStream(); // Stop any existing stream
currentStream = streamReader;
new Thread(currentStream).start();
isStreaming = true;
}
public void stopStream() {
if (currentStream != null) {
currentStream.stop();
currentStream = null;
}
isStreaming = false;
videoLabel.setIcon(null);
}
private void takeSnapshot() {
if (videoLabel.getIcon() == null) return;
try {
BufferedImage image = new BufferedImage(
videoLabel.getWidth(),
videoLabel.getHeight(),
BufferedImage.TYPE_INT_RGB
);
Graphics g = image.getGraphics();
videoLabel.paint(g);
g.dispose();
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
File outputFile = new File(String.format("snapshot_%s_%s.jpg", cameraName.replaceAll("[^a-zA-Z0-9]", "_"), timestamp));
ImageIO.write(image, "jpg", outputFile);
JOptionPane.showMessageDialog(this,
"Snapshot saved: " + outputFile.getName(),
"Snapshot Saved",
JOptionPane.INFORMATION_MESSAGE);
} catch (Exception e) {
JOptionPane.showMessageDialog(this,
"Error saving snapshot: " + e.getMessage(),
"Error",
JOptionPane.ERROR_MESSAGE);
}
}
public void updateFrame(Image frame) {
if (frame != null) {
videoLabel.setIcon(new ImageIcon(frame));
}
}
public String getCameraName() {
return cameraName;
}
public boolean isStreaming() {
return isStreaming;
}
public StreamReader getCurrentStream() {
return currentStream;
}
}

View File

@@ -0,0 +1,240 @@
package com.jsca;
import com.github.sarxos.webcam.Webcam;
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class CameraViewer extends JFrame {
private final JPanel gridPanel;
private final CameraManager cameraManager;
private final JLabel statusLabel;
public CameraViewer() {
setTitle("Security Camera Viewer");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setPreferredSize(new Dimension(1280, 960));
// Create grid panel for cameras
gridPanel = new JPanel(new GridLayout(2, 2, 5, 5));
gridPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
// Create status bar
statusLabel = new JLabel("Ready");
statusLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
// Initialize camera manager
cameraManager = new CameraManager(gridPanel);
// Create menu bar
setJMenuBar(createMenuBar());
// Add components to frame
add(gridPanel, BorderLayout.CENTER);
add(statusLabel, BorderLayout.SOUTH);
// Add window listener for cleanup
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
cameraManager.stopAllCameras();
}
});
pack();
setLocationRelativeTo(null);
}
private JMenuBar createMenuBar() {
JMenuBar menuBar = new JMenuBar();
// File Menu
JMenu fileMenu = new JMenu("File");
JMenuItem loadSetup = new JMenuItem("Load Camera Setup");
JMenuItem saveSetup = new JMenuItem("Save Camera Setup");
JMenuItem exit = new JMenuItem("Exit");
loadSetup.addActionListener(e -> cameraManager.loadConfiguration());
saveSetup.addActionListener(e -> cameraManager.saveConfiguration());
exit.addActionListener(e -> {
cameraManager.stopAllCameras();
dispose();
});
fileMenu.add(loadSetup);
fileMenu.add(saveSetup);
fileMenu.addSeparator();
fileMenu.add(exit);
// Camera Menu
JMenu cameraMenu = new JMenu("Camera");
JMenuItem addNetwork = new JMenuItem("Add Network Camera");
JMenuItem addLocal = new JMenuItem("Add Local Camera");
JMenuItem removeCamera = new JMenuItem("Remove Camera");
JMenuItem restartCamera = new JMenuItem("Restart Camera");
addNetwork.addActionListener(e -> addNetworkCamera());
addLocal.addActionListener(e -> addLocalCamera());
removeCamera.addActionListener(e -> removeSelectedCamera());
restartCamera.addActionListener(e -> restartSelectedCamera());
cameraMenu.add(addNetwork);
cameraMenu.add(addLocal);
cameraMenu.addSeparator();
cameraMenu.add(removeCamera);
cameraMenu.add(restartCamera);
// Help Menu
JMenu helpMenu = new JMenu("Help");
JMenuItem about = new JMenuItem("About");
JMenuItem instructions = new JMenuItem("Instructions");
about.addActionListener(e -> showAboutDialog());
instructions.addActionListener(e -> showInstructions());
helpMenu.add(about);
helpMenu.add(instructions);
menuBar.add(fileMenu);
menuBar.add(cameraMenu);
menuBar.add(helpMenu);
return menuBar;
}
private void addNetworkCamera() {
JPanel panel = new JPanel(new GridLayout(0, 2, 5, 5));
JTextField urlField = new JTextField();
JTextField usernameField = new JTextField();
JPasswordField passwordField = new JPasswordField();
SpinnerNumberModel positionModel = new SpinnerNumberModel(0, 0, 3, 1);
JSpinner positionSpinner = new JSpinner(positionModel);
panel.add(new JLabel("URL:"));
panel.add(urlField);
panel.add(new JLabel("Username:"));
panel.add(usernameField);
panel.add(new JLabel("Password:"));
panel.add(passwordField);
panel.add(new JLabel("Position (0-3):"));
panel.add(positionSpinner);
int result = JOptionPane.showConfirmDialog(this, panel,
"Add Network Camera", JOptionPane.OK_CANCEL_OPTION);
if (result == JOptionPane.OK_OPTION) {
String url = urlField.getText();
int position = (Integer) positionSpinner.getValue();
if (!url.trim().isEmpty()) {
String name = "Network Camera " + (position + 1);
NetworkStreamReader reader = new NetworkStreamReader(
cameraManager.getCameraPanel(position),
url
);
cameraManager.addCamera(name, reader, position);
updateStatus("Added network camera at position " + position);
}
}
}
private void addLocalCamera() {
java.util.List<Webcam> webcams = Webcam.getWebcams();
if (webcams.isEmpty()) {
JOptionPane.showMessageDialog(this,
"No webcams detected",
"Error",
JOptionPane.ERROR_MESSAGE);
return;
}
JPanel panel = new JPanel(new GridLayout(0, 2, 5, 5));
String[] options = webcams.stream()
.map(Webcam::getName)
.toArray(String[]::new);
JComboBox<String> webcamBox = new JComboBox<>(options);
SpinnerNumberModel positionModel = new SpinnerNumberModel(0, 0, 3, 1);
JSpinner positionSpinner = new JSpinner(positionModel);
panel.add(new JLabel("Select Webcam:"));
panel.add(webcamBox);
panel.add(new JLabel("Position (0-3):"));
panel.add(positionSpinner);
int result = JOptionPane.showConfirmDialog(this, panel,
"Add Local Camera", JOptionPane.OK_CANCEL_OPTION);
if (result == JOptionPane.OK_OPTION) {
String selected = (String) webcamBox.getSelectedItem();
int position = (Integer) positionSpinner.getValue();
if (selected != null) {
String name = "Local Camera " + (position + 1) + " (" + selected + ")";
WebcamStreamReader reader = new WebcamStreamReader(
cameraManager.getCameraPanel(position)
);
cameraManager.addCamera(name, reader, position);
updateStatus("Added local camera at position " + position);
}
}
}
private void removeSelectedCamera() {
SpinnerNumberModel positionModel = new SpinnerNumberModel(0, 0, 3, 1);
JSpinner positionSpinner = new JSpinner(positionModel);
int result = JOptionPane.showConfirmDialog(this,
positionSpinner,
"Select Camera Position to Remove (0-3)",
JOptionPane.OK_CANCEL_OPTION);
if (result == JOptionPane.OK_OPTION) {
int position = (Integer) positionSpinner.getValue();
cameraManager.removeCamera(position);
updateStatus("Removed camera at position " + position);
}
}
private void restartSelectedCamera() {
SpinnerNumberModel positionModel = new SpinnerNumberModel(0, 0, 3, 1);
JSpinner positionSpinner = new JSpinner(positionModel);
int result = JOptionPane.showConfirmDialog(this,
positionSpinner,
"Select Camera Position to Restart (0-3)",
JOptionPane.OK_CANCEL_OPTION);
if (result == JOptionPane.OK_OPTION) {
int position = (Integer) positionSpinner.getValue();
cameraManager.restartCamera(position);
updateStatus("Restarted camera at position " + position);
}
}
private void showAboutDialog() {
JOptionPane.showMessageDialog(this,
"Security Camera Viewer\nVersion 1.0\n\n" +
"A multi-camera security monitoring application\n" +
"Supports both network and local USB cameras",
"About",
JOptionPane.INFORMATION_MESSAGE);
}
private void showInstructions() {
JOptionPane.showMessageDialog(this,
"Instructions:\n\n" +
"1. Use the Camera menu to add network or local cameras\n" +
"2. Select a position (0-3) for each camera\n" +
"3. Use the File menu to save/load your camera setup\n" +
"4. Each camera panel has its own snapshot button\n\n" +
"Network Camera URLs should be in MJPEG format\n" +
"Example: http://camera-ip:port/video",
"Instructions",
JOptionPane.INFORMATION_MESSAGE);
}
private void updateStatus(String message) {
statusLabel.setText(message);
}
}

View File

@@ -0,0 +1,12 @@
package com.jsca;
import javax.swing.SwingUtilities;
public class Main {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
CameraViewer viewer = new CameraViewer();
viewer.setVisible(true);
});
}
}

View File

@@ -0,0 +1,109 @@
package com.jsca;
import javax.imageio.ImageIO;
import javax.swing.SwingUtilities;
import java.awt.Image;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
public class NetworkStreamReader implements StreamReader {
private final CameraPanel panel;
private final String streamUrl;
private volatile boolean running = true;
private HttpURLConnection connection;
public NetworkStreamReader(CameraPanel panel, String streamUrl) {
this.panel = panel;
this.streamUrl = streamUrl;
}
@Override
public void run() {
try {
URL url = new URL(streamUrl);
connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
try (BufferedInputStream in = new BufferedInputStream(connection.getInputStream())) {
byte[] buffer = new byte[8192];
int bytesRead;
byte[] imageBuffer = new byte[1024 * 1024]; // 1MB buffer for JPEG
int imagePos = 0;
boolean foundHeader = false;
while (running) {
bytesRead = in.read(buffer);
if (bytesRead == -1) break;
for (int i = 0; i < bytesRead; i++) {
// Look for JPEG header (0xFF, 0xD8)
if (!foundHeader && i < bytesRead - 1) {
if (buffer[i] == (byte) 0xFF && buffer[i + 1] == (byte) 0xD8) {
imagePos = 0;
foundHeader = true;
}
}
if (foundHeader) {
imageBuffer[imagePos++] = buffer[i];
// Look for JPEG footer (0xFF, 0xD9)
if (imagePos > 1 &&
imageBuffer[imagePos - 2] == (byte) 0xFF &&
imageBuffer[imagePos - 1] == (byte) 0xD9) {
// We have a complete JPEG image
byte[] imageData = Arrays.copyOf(imageBuffer, imagePos);
processImage(imageData);
foundHeader = false;
}
if (imagePos >= imageBuffer.length) {
// Buffer overflow, reset
foundHeader = false;
}
}
}
}
}
} catch (Exception e) {
if (running) {
SwingUtilities.invokeLater(() -> {
javax.swing.JOptionPane.showMessageDialog(panel,
"Error reading from network camera: " + e.getMessage(),
"Network Camera Error",
javax.swing.JOptionPane.ERROR_MESSAGE);
});
}
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
private void processImage(byte[] imageData) {
try {
Image image = ImageIO.read(new ByteArrayInputStream(imageData));
if (image != null) {
SwingUtilities.invokeLater(() -> panel.updateFrame(image));
}
Thread.sleep(33); // ~30 FPS
} catch (IOException | InterruptedException e) {
// Ignore individual frame errors
}
}
@Override
public void stop() {
running = false;
if (connection != null) {
connection.disconnect();
}
}
}

View File

@@ -0,0 +1,5 @@
package com.jsca;
public interface StreamReader extends Runnable {
void stop();
}

View File

@@ -0,0 +1,53 @@
package com.jsca;
import com.github.sarxos.webcam.Webcam;
import java.awt.Dimension;
import java.awt.Image;
import javax.swing.SwingUtilities;
public class WebcamStreamReader implements StreamReader {
private final CameraPanel panel;
private volatile boolean running = true;
private Webcam webcam;
public WebcamStreamReader(CameraPanel panel) {
this.panel = panel;
}
@Override
public void run() {
try {
webcam = Webcam.getDefault();
if (webcam == null) {
throw new RuntimeException("No webcam detected");
}
webcam.setViewSize(new Dimension(640, 480));
webcam.open();
while (running && webcam.isOpen()) {
final Image image = webcam.getImage();
if (image != null) {
SwingUtilities.invokeLater(() -> panel.updateFrame(image));
}
Thread.sleep(33); // ~30 FPS
}
} catch (Exception e) {
SwingUtilities.invokeLater(() -> {
javax.swing.JOptionPane.showMessageDialog(panel,
"Error reading from webcam: " + e.getMessage(),
"Webcam Error",
javax.swing.JOptionPane.ERROR_MESSAGE);
});
} finally {
if (webcam != null && webcam.isOpen()) {
webcam.close();
}
}
}
@Override
public void stop() {
running = false;
}
}