commit cceb0d44e8b918530d6b06e1ff5ccd178e422231 Author: rattatwinko Date: Tue May 27 16:34:09 2025 +0200 initial diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..c5f3f6b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "interactive" +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..9a812a8 --- /dev/null +++ b/README.md @@ -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. \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..2ed84c0 --- /dev/null +++ b/pom.xml @@ -0,0 +1,83 @@ + + + 4.0.0 + + com.jsca + security-camera-app + 1.0-SNAPSHOT + + + 11 + 11 + UTF-8 + + + + + + com.github.sarxos + webcam-capture + 0.3.12 + + + + + org.slf4j + slf4j-api + 1.7.32 + + + + + org.slf4j + slf4j-simple + 1.7.32 + + + + + com.google.code.gson + gson + 2.10.1 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 11 + 11 + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.3.0 + + + jar-with-dependencies + + + + com.jsca.Main + + + + + + package + + single + + + + + + + \ No newline at end of file diff --git a/snapshot_20250527_161519.jpg b/snapshot_20250527_161519.jpg new file mode 100644 index 0000000..71869d7 Binary files /dev/null and b/snapshot_20250527_161519.jpg differ diff --git a/src/main/java/com/jsca/CameraManager.java b/src/main/java/com/jsca/CameraManager.java new file mode 100644 index 0000000..946e704 --- /dev/null +++ b/src/main/java/com/jsca/CameraManager.java @@ -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 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 configs = new ArrayList<>(); + for (Map.Entry 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 configs = gson.fromJson(reader, + new TypeToken>(){}.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 + } +} \ No newline at end of file diff --git a/src/main/java/com/jsca/CameraPanel.java b/src/main/java/com/jsca/CameraPanel.java new file mode 100644 index 0000000..bde55a9 --- /dev/null +++ b/src/main/java/com/jsca/CameraPanel.java @@ -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; + } +} \ No newline at end of file diff --git a/src/main/java/com/jsca/CameraViewer.java b/src/main/java/com/jsca/CameraViewer.java new file mode 100644 index 0000000..0be9ecd --- /dev/null +++ b/src/main/java/com/jsca/CameraViewer.java @@ -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 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 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); + } +} \ No newline at end of file diff --git a/src/main/java/com/jsca/Main.java b/src/main/java/com/jsca/Main.java new file mode 100644 index 0000000..284aac2 --- /dev/null +++ b/src/main/java/com/jsca/Main.java @@ -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); + }); + } +} \ No newline at end of file diff --git a/src/main/java/com/jsca/NetworkStreamReader.java b/src/main/java/com/jsca/NetworkStreamReader.java new file mode 100644 index 0000000..6324b6a --- /dev/null +++ b/src/main/java/com/jsca/NetworkStreamReader.java @@ -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(); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/jsca/StreamReader.java b/src/main/java/com/jsca/StreamReader.java new file mode 100644 index 0000000..8e5c0ac --- /dev/null +++ b/src/main/java/com/jsca/StreamReader.java @@ -0,0 +1,5 @@ +package com.jsca; + +public interface StreamReader extends Runnable { + void stop(); +} \ No newline at end of file diff --git a/src/main/java/com/jsca/WebcamStreamReader.java b/src/main/java/com/jsca/WebcamStreamReader.java new file mode 100644 index 0000000..f840e2e --- /dev/null +++ b/src/main/java/com/jsca/WebcamStreamReader.java @@ -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; + } +} \ No newline at end of file diff --git a/target/classes/com/jsca/CameraManager$1.class b/target/classes/com/jsca/CameraManager$1.class new file mode 100644 index 0000000..5d1025a Binary files /dev/null and b/target/classes/com/jsca/CameraManager$1.class differ diff --git a/target/classes/com/jsca/CameraManager$CameraConfig.class b/target/classes/com/jsca/CameraManager$CameraConfig.class new file mode 100644 index 0000000..93c2691 Binary files /dev/null and b/target/classes/com/jsca/CameraManager$CameraConfig.class differ diff --git a/target/classes/com/jsca/CameraManager.class b/target/classes/com/jsca/CameraManager.class new file mode 100644 index 0000000..e1dcbd4 Binary files /dev/null and b/target/classes/com/jsca/CameraManager.class differ diff --git a/target/classes/com/jsca/CameraPanel.class b/target/classes/com/jsca/CameraPanel.class new file mode 100644 index 0000000..9d724ea Binary files /dev/null and b/target/classes/com/jsca/CameraPanel.class differ diff --git a/target/classes/com/jsca/CameraViewer$1.class b/target/classes/com/jsca/CameraViewer$1.class new file mode 100644 index 0000000..f086b80 Binary files /dev/null and b/target/classes/com/jsca/CameraViewer$1.class differ diff --git a/target/classes/com/jsca/CameraViewer.class b/target/classes/com/jsca/CameraViewer.class new file mode 100644 index 0000000..b4ace42 Binary files /dev/null and b/target/classes/com/jsca/CameraViewer.class differ diff --git a/target/classes/com/jsca/Main.class b/target/classes/com/jsca/Main.class new file mode 100644 index 0000000..c32667e Binary files /dev/null and b/target/classes/com/jsca/Main.class differ diff --git a/target/classes/com/jsca/NetworkStreamReader.class b/target/classes/com/jsca/NetworkStreamReader.class new file mode 100644 index 0000000..f9077e2 Binary files /dev/null and b/target/classes/com/jsca/NetworkStreamReader.class differ diff --git a/target/classes/com/jsca/StreamReader.class b/target/classes/com/jsca/StreamReader.class new file mode 100644 index 0000000..8c514d0 Binary files /dev/null and b/target/classes/com/jsca/StreamReader.class differ diff --git a/target/classes/com/jsca/WebcamStreamReader.class b/target/classes/com/jsca/WebcamStreamReader.class new file mode 100644 index 0000000..b577b49 Binary files /dev/null and b/target/classes/com/jsca/WebcamStreamReader.class differ