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.binding.LibVlc;
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;
012import uk.co.caprica.vlcj.runtime.RuntimeUtil;
013
014import com.github.sarxos.webcam.WebcamDevice;
015import com.github.sarxos.webcam.WebcamDiscoverySupport;
016import com.github.sarxos.webcam.WebcamDriver;
017import com.github.sarxos.webcam.util.OsUtils;
018import com.sun.jna.Native;
019
020
021/**
022 * This is capture driver which uses <code>vlcj</code> library to gain access to
023 * the camera device.
024 * 
025 * @author Bartosz Firyn (SarXos)
026 * @see http://www.capricasoftware.co.uk/projects/vlcj/index.html
027 */
028public class VlcjDriver implements WebcamDriver, WebcamDiscoverySupport {
029
030        /**
031         * Default webcam discovery scan interval in milliseconds.
032         */
033        public static final long DEFAULT_SCAN_INTERVAL = 3000;
034
035        /**
036         * Are natives initialized.
037         */
038        private static final AtomicBoolean initialized = new AtomicBoolean();
039
040        /**
041         * The scan interval.
042         */
043        private long scanInterval = -1;
044
045        public VlcjDriver() {
046                if (OsUtils.getOS() == OsUtils.WIN) {
047                        System.err.println(String.format("WARNING: %s does not support Windows platform", getClass().getSimpleName()));
048                }
049                initialize();
050        }
051
052        /**
053         * Initialize natives.
054         */
055        protected static void initialize() {
056                initialize(true);
057        }
058
059        /**
060         * Initialize natives. If argument is true the natives are being loaded. In
061         * case of false this method do nothing. It's used mostly in unit tests.
062         * 
063         * @param load the control to decide whether to load natives or ignore them
064         */
065        protected static void initialize(boolean load) {
066                if (load && initialized.compareAndSet(false, true)) {
067                        Native.loadLibrary(RuntimeUtil.getLibVlcLibraryName(), LibVlc.class);
068                }
069        }
070
071        @Override
072        public final List<WebcamDevice> getDevices() {
073
074                MediaPlayerFactory mediaPlayerFactory = createMediaPlayerFactory();
075                MediaDiscoverer videoMediaDiscoverer = mediaPlayerFactory.newVideoMediaDiscoverer();
076                MediaList videoDeviceList = videoMediaDiscoverer.getMediaList();
077
078                List<WebcamDevice> devices = new ArrayList<WebcamDevice>();
079                List<MediaListItem> videoDevices = videoDeviceList.items();
080
081                for (MediaListItem item : videoDevices) {
082                        devices.add(mediaListItemToDevice(item));
083                }
084
085                videoDeviceList.release();
086                videoMediaDiscoverer.release();
087                mediaPlayerFactory.release();
088
089                return devices;
090        }
091
092        /**
093         * Converts media list itemn into webcam device.
094         * 
095         * @param item the item to be converted to webcam device instance
096         * @return Webcam device created from media list item
097         */
098        protected WebcamDevice mediaListItemToDevice(MediaListItem item) {
099                return new VlcjDevice(item);
100        }
101
102        /**
103         * Creates media player factory.
104         * 
105         * @return New media player factory
106         */
107        protected MediaPlayerFactory createMediaPlayerFactory() {
108                return new MediaPlayerFactory();
109        }
110
111        @Override
112        public boolean isThreadSafe() {
113                return false;
114        }
115
116        @Override
117        public String toString() {
118                return getClass().getSimpleName();
119        }
120
121        @Override
122        public long getScanInterval() {
123                if (scanInterval <= 0) {
124                        return DEFAULT_SCAN_INTERVAL;
125                }
126                return scanInterval;
127        }
128
129        /**
130         * Set new scan interval. Value must be positive number. If negative or zero
131         * is used, then the corresponding getter will return default scan interval
132         * value.
133         * 
134         * @param scanInterval the new scan interval in milliseconds
135         * @see VlcjDriver#DEFAULT_SCAN_INTERVAL
136         */
137        public void setScanInterval(long scanInterval) {
138                this.scanInterval = scanInterval;
139        }
140
141        @Override
142        public boolean isScanPossible() {
143                return true;
144        }
145}