diff --git a/readme.md b/readme.md index b8e459f..19d6bf7 100644 --- a/readme.md +++ b/readme.md @@ -1,26 +1,47 @@ -# SWT-CCTV +# SWT-CCTV (Simple Watch Tool) A rather simple CCTV software which operates with Java. If you want to build this project on yourself, you will need IntelliJ (or any other IDE) and Maven! +If you are looking for a desktop like experience this is the software, it has its own windowing system! + +## Downloads: + +If you are looking for downloads then you are in luck! Currently there are Windows Executables and portable Jar Files in place! +Take a look at the [releases](https://rattatwinko.servecounterstrike.com/gitea/rattatwinko/swt-cctv/releases) page for the newest software releases! + +[Releases Page](https://rattatwinko.servecounterstrike.com/gitea/rattatwinko/swt-cctv/releases) ## Dependencies: - Webcam by Sarxos - Swing (AWT) -- _lwjgl (with opengl)_ → This is important for our goals of rendering on the GPU. - junit for testing stuff -- jcodec, in the future we will be recording using this - Jackson (fasterxml) → serializing the config for network cams +- JavaCV + - FFmpeg ### Future Plans: -They arent too big, i want one thing more and that is some more utilities in the camera window. +Implement some more JavaCV cause of performance. -Protable Jar which can be run with JRE 17 (already done but not too good!) +## Requirements: + +- JRE/JDK 1.8.00 - 25 ([Reccomended](https://adoptium.net/de/download?link=https%3A%2F%2Fgithub.com%2Fadoptium%2Ftemurin17-binaries%2Freleases%2Fdownload%2Fjdk-17.0.17%252B10%2FOpenJDK17U-jre_x64_windows_hotspot_17.0.17_10.msi&vendor=Adoptium)) + +| System Requirements | Minimum Requirements | Reccomended Requirements | +|--------------------- |---------------------------------------------------------------------------------------------------------------------------------------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| **_CPU_** | [Intel(R) Celeron(R) CPU G550 @ 2.60GHz](https://www.techpowerup.com/cpu-specs/celeron-g550.c1339) **_Or any Dual Core CPU_** | [Intel® Core™ i5-3470](https://www.intel.de/content/www/de/de/products/sku/68316/intel-core-i53470-processor-6m-cache-up-to-3-60-ghz/specifications.html) Or any **_Quad (or more) Core CPU_** | +| **_RAM_** | **2GB DDR3** | **4/8GB DDR3/4/5** (You can have **_more_** than this, _Java likes it_) | +| **_JRE_** | Java Runtime Enviroment: [1.8.000](https://javadl.oracle.com/webapps/download/AutoDL?BundleId=252905_0d06828d282343ea81775b28020a7cd3) | Java Runtime Enviroment _(or JDK)_: [17](https://adoptium.net/download?link=https%3A%2F%2Fgithub.com%2Fadoptium%2Ftemurin17-binaries%2Freleases%2Fdownload%2Fjdk-17.0.17%252B10%2FOpenJDK17U-jre_x64_windows_hotspot_17.0.17_10.msi&vendor=Adoptium) | +| **_Disk Space_** | **_100Mb of HDD/SSD_** Space for the Program (currently **40.3Mb**) ; _For Recording more is needed_ | _100Mb SSD Space_ ; For Recording more than 100Mb , this depends on how many cameras you have | + +**Note: This was tested on these CPU'S!** ### Future Plans: - [x] basic network cam interfacing -- [ ] better multiplexer (or whatever the viewport is called in cctv) +- [x] better multiplexer (or whatever the viewport is called in cctv) - [x] Protable .jar which can be run with **JRE 17** +- [ / ] Performance stabilisation (currently it is in place, and this app can be run on shitty hardware, but it can be better (current low is a celeron g550)) +- [ ] JavaCV integration for cameras ### Author(s): diff --git a/src/main/java/io/swtc/Main.java b/src/main/java/io/swtc/Main.java index c62e48e..4eb4f76 100644 --- a/src/main/java/io/swtc/Main.java +++ b/src/main/java/io/swtc/Main.java @@ -2,16 +2,9 @@ package io.swtc; import javax.swing.*; -import io.swtc.proccessing.ui.IconSetter; -import io.swtc.proccessing.ui.ShowError; - public class Main { public static void main(String[] args) { -// for (int i = 0; i < args.length; i++) { -// System.out.println("Arg " + i + ": " + args[i]); -// } - try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e) { /* Do nothing */ } diff --git a/src/main/java/io/swtc/SwingCCTVManager.java b/src/main/java/io/swtc/SwingCCTVManager.java index 716786d..9327aa5 100644 --- a/src/main/java/io/swtc/SwingCCTVManager.java +++ b/src/main/java/io/swtc/SwingCCTVManager.java @@ -45,7 +45,6 @@ public class SwingCCTVManager { private final DefaultTableModel tableModel; private final SwingIFrame IFrame; private boolean isRefreshing = false; - public SwingCCTVManager() { frame = new JFrame("Dashboard"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); diff --git a/src/main/java/io/swtc/SwingIFrame.java b/src/main/java/io/swtc/SwingIFrame.java index 0516899..bd41aeb 100644 --- a/src/main/java/io/swtc/SwingIFrame.java +++ b/src/main/java/io/swtc/SwingIFrame.java @@ -3,6 +3,7 @@ package io.swtc; import com.github.sarxos.webcam.Webcam; import io.swtc.proccessing.ui.IconSetter; import io.swtc.proccessing.ui.desktop.DIM; +import io.swtc.proccessing.ui.desktop.debug.Profiler; import io.swtc.proccessing.ui.desktop.evidence.EvidenceExportFrame; import io.swtc.proccessing.ui.iframe.*; import javax.swing.*; @@ -42,6 +43,7 @@ public class SwingIFrame { desktopIconManager = new DIM(desktopPane); setupDesktopExportFrame(); + setupProfiler(); setupFullscreenToggle(); setupBlackBg(); @@ -59,6 +61,17 @@ public class SwingIFrame { ); } + private void setupProfiler() { + desktopIconManager.addIcon( + "Profiler", + IconSetter.getDbg_icon(), + () -> { + SwingUtilities.invokeLater(() -> { + Profiler.showFrame(new Profiler(mainFrame)); + }); + }); + } + public void addCameraInternalFrame(Webcam webcam) { CameraInternalFrame cameraFrame = new CameraInternalFrame(webcam, this::handleEffectsRequest); diff --git a/src/main/java/io/swtc/proccessing/ui/IconSetter.java b/src/main/java/io/swtc/proccessing/ui/IconSetter.java index 57f741b..7347f2a 100644 --- a/src/main/java/io/swtc/proccessing/ui/IconSetter.java +++ b/src/main/java/io/swtc/proccessing/ui/IconSetter.java @@ -2,20 +2,25 @@ package io.swtc.proccessing.ui; import javax.swing.*; import java.awt.*; -import java.io.InputStream; import java.net.URL; import java.util.Objects; +/* vital boilerplate class, shouldve made it better but idk. */ public class IconSetter { private static Image ICON_IMAGE; private static ImageIcon ICON_ICON; private static Image effects_icon; + private static ImageIcon dbg_icon; + /* this is used for the app icon itself (the one in tb) */ public static Image getIcon() { if (ICON_IMAGE == null) { URL url = IconSetter.class.getResource("/icons/artwork.png"); - if (url == null) throw new RuntimeException("Icon not found: /icons/artwork.png"); + if (Objects.isNull(url)) { + ShowError.error(null,"Icon","Icon (Type: Image) failed, NULL!"); + throw new RuntimeException("NULL!"); + } ICON_IMAGE = Toolkit.getDefaultToolkit().getImage(url); } return ICON_IMAGE; @@ -24,7 +29,10 @@ public class IconSetter { public static Image getEffectIcon() { if (Objects.isNull(effects_icon)) { URL url = IconSetter.class.getResource("/icons/effectsframe.png"); - if (Objects.isNull(url)) ShowError.error(null,"Error","Icon not found"); + if (Objects.isNull(url)) { + ShowError.error(null,"Icon","Effects icon was Null! (Type Image)"); + throw new RuntimeException("NULL!"); + } effects_icon = Toolkit.getDefaultToolkit().getImage(url); } return effects_icon; @@ -33,7 +41,10 @@ public class IconSetter { public static ImageIcon getIconAsImageIcon() { if (ICON_ICON == null) { URL url = IconSetter.class.getResource("/icons/artwork.png"); - if (url == null) throw new RuntimeException("Icon not found: /icons/artwork.png"); + if (Objects.isNull(url)) { + ShowError.error(null,"Icon","Icon not found!, NULL! (Type ImageIcon)"); + throw new RuntimeException("NULL!"); + } ICON_ICON = new ImageIcon(url); // separate variable for ImageIcon } return ICON_ICON; @@ -42,9 +53,24 @@ public class IconSetter { public static ImageIcon getSaveIconAsImageIcon() { if (Objects.isNull(ICON_ICON)) { URL url = IconSetter.class.getResource("/icons/save.png"); - if (Objects.isNull(url)) throw new RuntimeException("Icon not found: /icons/save.ico"); + if (Objects.isNull(url)) { + ShowError.error(null,"Icon","getSaveIconAsImageIcon failed, NULL! (Type ImageIcon)"); + throw new RuntimeException("NULL!"); + } ICON_ICON = new ImageIcon(url); } return ICON_ICON; } + + public static ImageIcon getDbg_icon() { + if (Objects.isNull(dbg_icon)) { + URL url = IconSetter.class.getResource("/icons/icondbg-7.png"); + if (Objects.isNull(url)) { + ShowError.error(null, "Icon", "getDbg_icon, object url was null (Type ImageIcon)"); + throw new RuntimeException("NULL!"); + } + dbg_icon = new ImageIcon(url); + } + return dbg_icon; + } } diff --git a/src/main/java/io/swtc/proccessing/ui/desktop/debug/Profiler.java b/src/main/java/io/swtc/proccessing/ui/desktop/debug/Profiler.java new file mode 100644 index 0000000..3f6ec7b --- /dev/null +++ b/src/main/java/io/swtc/proccessing/ui/desktop/debug/Profiler.java @@ -0,0 +1,169 @@ +package io.swtc.proccessing.ui.desktop.debug; + +import io.swtc.proccessing.ui.IconSetter; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import javax.swing.table.DefaultTableModel; +import java.awt.*; +import java.lang.management.*; +import java.util.List; + +/* simple profiler to see memory usage, this isnt too important but certainly useful */ +public class Profiler extends JFrame { + + private final MemoryMXBean memoryMXBean = + ManagementFactory.getMemoryMXBean(); + private final ThreadMXBean threadMXBean = + ManagementFactory.getThreadMXBean(); + private final List pools = + ManagementFactory.getMemoryPoolMXBeans(); + private final List gcs = + ManagementFactory.getGarbageCollectorMXBeans(); + private final List buffers = + ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class); + + private final JLabel heapLabel = new JLabel(); + private final JLabel nonHeapLabel = new JLabel(); + private final JLabel threadLabel = new JLabel(); + + private final DefaultTableModel poolModel = + new DefaultTableModel( + new String[]{"Pool", "Type", "Used (MB)", "Committed (MB)", "Max (MB)"}, + 0 + ); + + private final DefaultTableModel gcModel = + new DefaultTableModel( + new String[]{"GC", "Collections", "Time (ms)"}, + 0 + ); + + private final DefaultTableModel bufferModel = + new DefaultTableModel( + new String[]{"Buffer", "Used (MB)", "Count"}, + 0 + ); + + public Profiler(JFrame parent) { + setTitle("Profiler"); + setSize(750, 400); + setLocationRelativeTo(parent); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + + ImageIcon ico = IconSetter.getDbg_icon(); + setIconImage(ico.getImage()); + + JPanel root = new JPanel(new BorderLayout(10, 10)); + root.setBorder(new EmptyBorder(10, 10, 10, 10)); + setContentPane(root); + + JPanel summary = new JPanel(); + summary.setLayout(new BoxLayout(summary, BoxLayout.Y_AXIS)); + + summary.add(heapLabel); + summary.add(nonHeapLabel); + summary.add(threadLabel); + + root.add(summary, BorderLayout.NORTH); + + JTabbedPane tabs = new JTabbedPane(); + + tabs.add("Memory Pools", new JScrollPane(new JTable(poolModel))); + tabs.add("GC", new JScrollPane(new JTable(gcModel))); + tabs.add("Buffers", new JScrollPane(new JTable(bufferModel))); + + root.add(tabs, BorderLayout.CENTER); + + Timer timer = new Timer(1000, e -> update()); + timer.start(); + + update(); + } + + private void update() { + updateSummary(); + updatePools(); + updateGC(); + updateBuffers(); + } + + private void updateSummary() { + MemoryUsage heap = memoryMXBean.getHeapMemoryUsage(); + MemoryUsage nonHeap = memoryMXBean.getNonHeapMemoryUsage(); + + heapLabel.setText(String.format( + "Heap: used %d MB / committed %d MB / max %d MB", + mb(heap.getUsed()), + mb(heap.getCommitted()), + mb(heap.getMax()) + )); + + nonHeapLabel.setText(String.format( + "Non-Heap: used %d MB / committed %d MB", + mb(nonHeap.getUsed()), + mb(nonHeap.getCommitted()) + )); + + int threads = threadMXBean.getThreadCount(); + int peak = threadMXBean.getPeakThreadCount(); + int daemons = threadMXBean.getDaemonThreadCount(); + + threadLabel.setText(String.format( + "Threads: %d live (%d daemon, peak %d)", + threads, daemons, peak + )); + } + + private void updatePools() { + poolModel.setRowCount(0); + + for (MemoryPoolMXBean pool : pools) { + MemoryUsage u = pool.getUsage(); + if (u == null) continue; + + poolModel.addRow(new Object[]{ + pool.getName(), + pool.getType(), + mb(u.getUsed()), + mb(u.getCommitted()), + mb(u.getMax()) + }); + } + } + + private void updateGC() { + gcModel.setRowCount(0); + + for (GarbageCollectorMXBean gc : gcs) { + gcModel.addRow(new Object[]{ + gc.getName(), + gc.getCollectionCount(), + gc.getCollectionTime() + }); + } + } + + private void updateBuffers() { + bufferModel.setRowCount(0); + + for (BufferPoolMXBean b : buffers) { + bufferModel.addRow(new Object[]{ + b.getName(), + mb(b.getMemoryUsed()), + b.getCount() + }); + } + } + + /* Conversion logic for byte -> mb */ + public long mb(long bytes) { + return bytes < 0 ? -1 : bytes / 1024 / 1024; + } + + public static void showFrame(JFrame parent) { + SwingUtilities.invokeLater(() -> + new Profiler(parent).setVisible(true) + ); + } +} diff --git a/src/main/resources/icons/icondbg-7.png b/src/main/resources/icons/icondbg-7.png new file mode 100644 index 0000000..72f975b Binary files /dev/null and b/src/main/resources/icons/icondbg-7.png differ