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}