/* * ShootOFF - Software for Laser Dry Fire Training * Copyright (C) 2016 phrack * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.shootoff.camera; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.github.sarxos.webcam.Webcam; import com.github.sarxos.webcam.WebcamCompositeDriver; import com.github.sarxos.webcam.ds.buildin.WebcamDefaultDriver; import com.github.sarxos.webcam.ds.ipcam.IpCamDevice; import com.github.sarxos.webcam.ds.ipcam.IpCamDriver; import com.github.sarxos.webcam.ds.v4l4j.V4l4jDriver; import com.shootoff.camera.cameratypes.Camera; import com.shootoff.camera.cameratypes.IpCamera; import com.shootoff.camera.cameratypes.SarxosCaptureCamera; import com.shootoff.util.SystemInfo; public final class CameraFactory { private static final Logger logger = LoggerFactory.getLogger(CameraFactory.class); // These are used in a hack to get this code to work on Mac. // On Mac several of the webcam-capture API's can only be // called on the main thread before a JavaFX thread is started // or the library will hopeless hang and take ShootOFF with it. // Our solution is to cache the things we need that will hang // the program on start-up. This has the side effect that the // cameras that are known when ShootOFF starts are the only // ones it will ever know on Mac. private static final boolean isMac; private static Webcam defaultWebcam = null; private static List<Camera> knownWebcams; private static List<Camera> openCameras = Collections.synchronizedList(new ArrayList<>()); // This list contains cameras that are not discoverable by webcam-capture // because they are non-UVC cameras with no webcam-capture driver (e.g. // Omnitrack). Adding such cameras to this list ensures they are returned // by CameraFactory.getWebcams(). private final static List<Camera> registeredCameras = new ArrayList<>(); public static void registerCamera(Camera camera) { registeredCameras.add(camera); } public static class CompositeDriver extends WebcamCompositeDriver { public CompositeDriver() { super(); if (SystemInfo.isArm() && SystemInfo.isLinux()) { add(new V4l4jDriver()); } else { add(new WebcamDefaultDriver()); } add(new IpCamDriver()); } } static { Webcam.setDriver(new CompositeDriver()); if (SystemInfo.isMacOsX()) { isMac = true; defaultWebcam = Webcam.getDefault(); knownWebcams = new ArrayList<>(); for (final Webcam w : Webcam.getWebcams()) { final Camera c = (w.getDevice() instanceof IpCamDevice) ? new IpCamera(w) : new SarxosCaptureCamera(w.getName()); knownWebcams.add(c); } } else { isMac = false; defaultWebcam = null; knownWebcams = null; } } public static Optional<Camera> getDefault() { Camera defaultCam; if (isMac) { if (defaultWebcam == null) defaultCam = null; else defaultCam = new SarxosCaptureCamera(defaultWebcam.getName()); } else { final Webcam cam = Webcam.getDefault(); defaultCam = cam == null ? null : new SarxosCaptureCamera(cam.getName()); } if (defaultCam == null && !registeredCameras.isEmpty()) { defaultCam = registeredCameras.get(0); } return Optional.ofNullable(defaultCam); } public static List<Camera> getWebcams() { if (isMac) return knownWebcams; final List<Camera> webcams = new ArrayList<>(); int cameraIndex = 0; for (final Webcam w : Webcam.getWebcams()) { final Camera c; if (w.getDevice() instanceof IpCamDevice) c = new IpCamera(w); else c = new SarxosCaptureCamera(w.getName(), cameraIndex); synchronized (openCameras) { // If we already have an open instance of the camera // go ahead and reuse it in this list as opposed to // the newly created camera final int i = openCameras.indexOf(c); if (logger.isTraceEnabled()) logger.trace("Looking in openCameras for {} found at {}", w.getName(), i); if (i >= 0) { webcams.add(openCameras.get(i)); } else { webcams.add(c); } } cameraIndex++; } webcams.addAll(registeredCameras); return webcams; } public static void openCamerasRemove(Camera camera) { synchronized (openCameras) { openCameras.remove(camera); } } public static void openCamerasAdd(Camera camera) { synchronized (openCameras) { if (!openCameras.contains(camera)) openCameras.add(camera); } } public static boolean isMac() { return isMac; } }