001package com.github.sarxos.webcam.ds.vlcj;
002
003import java.util.ArrayList;
004import java.util.List;
005import java.util.concurrent.atomic.AtomicBoolean;
006
007import uk.co.caprica.vlcj.discovery.NativeDiscovery;
008import uk.co.caprica.vlcj.medialist.MediaList;
009import uk.co.caprica.vlcj.medialist.MediaListItem;
010import uk.co.caprica.vlcj.player.MediaPlayerFactory;
011import uk.co.caprica.vlcj.player.discoverer.MediaDiscoverer;
012
013import com.github.sarxos.webcam.WebcamDevice;
014import com.github.sarxos.webcam.WebcamDiscoverySupport;
015import com.github.sarxos.webcam.WebcamDriver;
016import com.github.sarxos.webcam.util.OsUtils;
017
018
019/**
020 * This is capture driver which uses <code>vlcj</code> library to gain access to the camera device.
021 * The library can be found at:<br>
022 * <br>
023 * http://www.capricasoftware.co.uk/projects/vlcj/index.html
024 *
025 * @author Bartosz Firyn (SarXos)
026 */
027public class VlcjDriver implements WebcamDriver, WebcamDiscoverySupport {
028
029        static {
030                if ("true".equals(System.getProperty("webcam.debug"))) {
031                        System.setProperty("vlcj.log", "DEBUG");
032                }
033        }
034
035        /**
036         * Default webcam discovery scan interval in milliseconds.
037         */
038        public static final long DEFAULT_SCAN_INTERVAL = 3000;
039
040        /**
041         * Are natives initialized.
042         */
043        private static final AtomicBoolean initialized = new AtomicBoolean();
044
045        /**
046         * Native library discoverer.
047         */
048        private static NativeDiscovery nativeDiscovery;
049
050        /**
051         * The scan interval.
052         */
053        private long scanInterval = -1;
054
055        /**
056         * Preconfigured media list items.
057         */
058        private final List<MediaListItem> mediaListItems;
059
060        public VlcjDriver() {
061                this(null);
062        }
063
064        public VlcjDriver(List<MediaListItem> mediaListItems) {
065                this.mediaListItems = mediaListItems;
066                initialize();
067        }
068
069        /**
070         * Initialize natives.
071         */
072        protected static void initialize() {
073                initialize(true);
074        }
075
076        /**
077         * Initialize natives. If argument is true the natives are being loaded. In case of false this
078         * method do nothing. It's used mostly in unit tests.
079         *
080         * @param load the control to decide whether to load natives or ignore them
081         */
082        protected static void initialize(boolean load) {
083                if (load && initialized.compareAndSet(false, true)) {
084                        boolean nativeFound = getNativeDiscovery().discover();
085                        if (!nativeFound) {
086                                throw new IllegalStateException("The libvlc native library has not been found");
087                        }
088                        // Native.loadLibrary(RuntimeUtil.getLibVlcLibraryName(), LibVlc.class);
089                }
090        }
091
092        @Override
093        public List<WebcamDevice> getDevices() {
094
095                if (OsUtils.getOS() == OsUtils.WIN) {
096                        System.err.println("WARNING: VLCj does not support webcam devices discovery on Windows platform");
097                }
098
099                List<WebcamDevice> devices = new ArrayList<WebcamDevice>();
100
101                if (mediaListItems != null) {
102
103                        for (MediaListItem item : mediaListItems) {
104                                devices.add(mediaListItemToDevice(item));
105                        }
106
107                } else {
108
109                        MediaPlayerFactory mediaPlayerFactory = createMediaPlayerFactory();
110                        MediaDiscoverer videoMediaDiscoverer = mediaPlayerFactory.newVideoMediaDiscoverer();
111                        MediaList videoDeviceList = videoMediaDiscoverer.getMediaList();
112                        List<MediaListItem> videoDevices = videoDeviceList.items();
113
114                        for (MediaListItem item : videoDevices) {
115                                devices.add(mediaListItemToDevice(item));
116                        }
117
118                        videoDeviceList.release();
119                        videoMediaDiscoverer.release();
120                        mediaPlayerFactory.release();
121                }
122
123                return devices;
124        }
125
126        /**
127         * Converts media list itemn into webcam device.
128         *
129         * @param item the item to be converted to webcam device instance
130         * @return Webcam device created from media list item
131         */
132        protected WebcamDevice mediaListItemToDevice(MediaListItem item) {
133                return new VlcjDevice(item);
134        }
135
136        /**
137         * Creates media player factory.
138         *
139         * @return New media player factory
140         */
141        protected MediaPlayerFactory createMediaPlayerFactory() {
142                return new MediaPlayerFactory();
143        }
144
145        @Override
146        public boolean isThreadSafe() {
147                return false;
148        }
149
150        @Override
151        public String toString() {
152                return getClass().getSimpleName();
153        }
154
155        @Override
156        public long getScanInterval() {
157                if (scanInterval <= 0) {
158                        return DEFAULT_SCAN_INTERVAL;
159                }
160                return scanInterval;
161        }
162
163        /**
164         * Set new scan interval. Value must be positive number. If negative or zero is used, then the
165         * corresponding getter will return default scan interval value.
166         *
167         * @param scanInterval the new scan interval in milliseconds
168         * @see VlcjDriver#DEFAULT_SCAN_INTERVAL
169         */
170        public void setScanInterval(long scanInterval) {
171                this.scanInterval = scanInterval;
172        }
173
174        @Override
175        public boolean isScanPossible() {
176                return OsUtils.getOS() != OsUtils.WIN;
177        }
178
179        protected static NativeDiscovery getNativeDiscovery() {
180                if (nativeDiscovery == null) {
181                        nativeDiscovery = new NativeDiscovery();
182                }
183                return nativeDiscovery;
184        }
185}