initial
This commit is contained in:
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"java.configuration.updateBuildConfiguration": "interactive"
|
||||
}
|
||||
65
README.md
Normal file
65
README.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Java Security Camera App
|
||||
|
||||
A Java desktop application for viewing live streams from both USB webcams and network MJPEG cameras.
|
||||
|
||||
## Features
|
||||
|
||||
- View live streams from USB webcams
|
||||
- Connect to network MJPEG cameras (e.g., DroidCamX)
|
||||
- Take snapshots of the current view
|
||||
- Simple and intuitive user interface
|
||||
- Support for multiple camera sources
|
||||
|
||||
## Requirements
|
||||
|
||||
- Java 11 or higher
|
||||
- Maven
|
||||
- USB webcam (for local camera support)
|
||||
- Network camera with MJPEG stream support (for network camera support)
|
||||
|
||||
## Building the Application
|
||||
|
||||
1. Clone the repository
|
||||
2. Navigate to the project directory
|
||||
3. Build with Maven:
|
||||
```bash
|
||||
mvn clean package
|
||||
```
|
||||
|
||||
## Running the Application
|
||||
|
||||
After building, run the application using:
|
||||
```bash
|
||||
java -jar target/security-camera-app-1.0-SNAPSHOT-jar-with-dependencies.jar
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
1. Launch the application
|
||||
2. Select the camera source from the dropdown:
|
||||
- USB Camera: Uses your computer's webcam
|
||||
- Network Camera: Connects to an MJPEG stream URL
|
||||
3. Click "Start" to begin streaming
|
||||
4. Use the "Snapshot" button to capture the current frame
|
||||
5. Click "Stop" to end the stream
|
||||
|
||||
### Using with Network Cameras
|
||||
|
||||
For network cameras, you'll need to provide the MJPEG stream URL. Common formats include:
|
||||
- DroidCamX: `http://[IP_ADDRESS]:4747/video`
|
||||
- Generic IP Camera: `http://[IP_ADDRESS]/video` or `http://[IP_ADDRESS]/mjpeg`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
1. No webcam detected:
|
||||
- Ensure your webcam is properly connected
|
||||
- Check if other applications are using the webcam
|
||||
|
||||
2. Network camera not connecting:
|
||||
- Verify the camera URL is correct
|
||||
- Ensure the camera is on the same network
|
||||
- Check if the camera supports MJPEG streaming
|
||||
|
||||
## License
|
||||
|
||||
This project is open source and available under the MIT License.
|
||||
83
pom.xml
Normal file
83
pom.xml
Normal file
@@ -0,0 +1,83 @@
|
||||
<?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.jsca</groupId>
|
||||
<artifactId>security-camera-app</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- Webcam Capture for USB cameras -->
|
||||
<dependency>
|
||||
<groupId>com.github.sarxos</groupId>
|
||||
<artifactId>webcam-capture</artifactId>
|
||||
<version>0.3.12</version>
|
||||
</dependency>
|
||||
|
||||
<!-- SLF4J API -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.32</version>
|
||||
</dependency>
|
||||
|
||||
<!-- SLF4J Simple binding -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>1.7.32</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Gson for JSON handling -->
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.10.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<source>11</source>
|
||||
<target>11</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<configuration>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||
</descriptorRefs>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>com.jsca.Main</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
BIN
snapshot_20250527_161519.jpg
Normal file
BIN
snapshot_20250527_161519.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
142
src/main/java/com/jsca/CameraManager.java
Normal file
142
src/main/java/com/jsca/CameraManager.java
Normal 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
|
||||
}
|
||||
}
|
||||
99
src/main/java/com/jsca/CameraPanel.java
Normal file
99
src/main/java/com/jsca/CameraPanel.java
Normal 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;
|
||||
}
|
||||
}
|
||||
240
src/main/java/com/jsca/CameraViewer.java
Normal file
240
src/main/java/com/jsca/CameraViewer.java
Normal 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);
|
||||
}
|
||||
}
|
||||
12
src/main/java/com/jsca/Main.java
Normal file
12
src/main/java/com/jsca/Main.java
Normal 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
109
src/main/java/com/jsca/NetworkStreamReader.java
Normal file
109
src/main/java/com/jsca/NetworkStreamReader.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
5
src/main/java/com/jsca/StreamReader.java
Normal file
5
src/main/java/com/jsca/StreamReader.java
Normal file
@@ -0,0 +1,5 @@
|
||||
package com.jsca;
|
||||
|
||||
public interface StreamReader extends Runnable {
|
||||
void stop();
|
||||
}
|
||||
53
src/main/java/com/jsca/WebcamStreamReader.java
Normal file
53
src/main/java/com/jsca/WebcamStreamReader.java
Normal 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;
|
||||
}
|
||||
}
|
||||
BIN
target/classes/com/jsca/CameraManager$1.class
Normal file
BIN
target/classes/com/jsca/CameraManager$1.class
Normal file
Binary file not shown.
BIN
target/classes/com/jsca/CameraManager$CameraConfig.class
Normal file
BIN
target/classes/com/jsca/CameraManager$CameraConfig.class
Normal file
Binary file not shown.
BIN
target/classes/com/jsca/CameraManager.class
Normal file
BIN
target/classes/com/jsca/CameraManager.class
Normal file
Binary file not shown.
BIN
target/classes/com/jsca/CameraPanel.class
Normal file
BIN
target/classes/com/jsca/CameraPanel.class
Normal file
Binary file not shown.
BIN
target/classes/com/jsca/CameraViewer$1.class
Normal file
BIN
target/classes/com/jsca/CameraViewer$1.class
Normal file
Binary file not shown.
BIN
target/classes/com/jsca/CameraViewer.class
Normal file
BIN
target/classes/com/jsca/CameraViewer.class
Normal file
Binary file not shown.
BIN
target/classes/com/jsca/Main.class
Normal file
BIN
target/classes/com/jsca/Main.class
Normal file
Binary file not shown.
BIN
target/classes/com/jsca/NetworkStreamReader.class
Normal file
BIN
target/classes/com/jsca/NetworkStreamReader.class
Normal file
Binary file not shown.
BIN
target/classes/com/jsca/StreamReader.class
Normal file
BIN
target/classes/com/jsca/StreamReader.class
Normal file
Binary file not shown.
BIN
target/classes/com/jsca/WebcamStreamReader.class
Normal file
BIN
target/classes/com/jsca/WebcamStreamReader.class
Normal file
Binary file not shown.
Reference in New Issue
Block a user