From e225d8f0bc5e7c04141b94a239ebd535b1d1f2a8 Mon Sep 17 00:00:00 2001 From: rattatwinko Date: Sun, 8 Feb 2026 17:00:05 +0100 Subject: [PATCH] Added Profiler + Some Fixes + Profiler.java new readme Signed-off-by: rattatwinko --- readme.md | 33 +++- src/main/java/io/swtc/Main.java | 7 - src/main/java/io/swtc/SwingCCTVManager.java | 1 - src/main/java/io/swtc/SwingIFrame.java | 13 ++ .../io/swtc/proccessing/ui/IconSetter.java | 36 +++- .../ui/desktop/debug/Profiler.java | 169 ++++++++++++++++++ src/main/resources/icons/icondbg-7.png | Bin 0 -> 3148 bytes 7 files changed, 240 insertions(+), 19 deletions(-) create mode 100644 src/main/java/io/swtc/proccessing/ui/desktop/debug/Profiler.java create mode 100644 src/main/resources/icons/icondbg-7.png 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 0000000000000000000000000000000000000000..72f975b7c86b363c0660187f3821af3b4e6e8479 GIT binary patch literal 3148 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 UVc-3BYQF)Pp}9e|o=fz90Aj}FlmGw# literal 0 HcmV?d00001