001 package com.github.sarxos.webcam; 002 003 import java.io.DataInputStream; 004 import java.io.DataOutputStream; 005 import java.io.File; 006 import java.io.FileInputStream; 007 import java.io.FileOutputStream; 008 import java.io.IOException; 009 import java.util.concurrent.atomic.AtomicBoolean; 010 011 import org.slf4j.Logger; 012 import org.slf4j.LoggerFactory; 013 014 015 /** 016 * This class is used as a global (system) lock preventing other processes from 017 * using the same camera while it's open. 018 * 019 * @author Bartosz Firyn (sarxos) 020 */ 021 public class WebcamLock { 022 023 /** 024 * Logger. 025 */ 026 private static final Logger LOG = LoggerFactory.getLogger(WebcamLock.class); 027 028 /** 029 * Update interval (ms). 030 */ 031 private static final long INTERVAL = 2000; 032 033 /** 034 * Used to update lock state. 035 * 036 * @author sarxos 037 */ 038 private class LockUpdater extends Thread { 039 040 public LockUpdater() { 041 super(); 042 setName(String.format("webcam-lock-[%s]", webcam.getName())); 043 setDaemon(true); 044 setUncaughtExceptionHandler(WebcamExceptionHandler.getInstance()); 045 } 046 047 @Override 048 public void run() { 049 do { 050 update(); 051 try { 052 Thread.sleep(INTERVAL); 053 } catch (InterruptedException e) { 054 LOG.debug("Lock updater has been interrupted"); 055 return; 056 } 057 } while (locked.get()); 058 } 059 060 } 061 062 /** 063 * And the Webcam we will be locking. 064 */ 065 private final Webcam webcam; 066 067 private Thread updater = null; 068 069 private AtomicBoolean locked = new AtomicBoolean(false); 070 071 private File lock = null; 072 073 /** 074 * Creates global webcam lock. 075 * 076 * @param webcam the webcam instance to be locked 077 */ 078 protected WebcamLock(Webcam webcam) { 079 super(); 080 this.webcam = webcam; 081 this.lock = new File(System.getProperty("java.io.tmpdir"), getLockName()); 082 } 083 084 private String getLockName() { 085 return String.format(".webcam-lock-%d", Math.abs(webcam.getName().hashCode())); 086 } 087 088 private void write(long value) { 089 090 String name = getLockName(); 091 092 File tmp = null; 093 DataOutputStream dos = null; 094 095 try { 096 tmp = File.createTempFile(name, ""); 097 098 dos = new DataOutputStream(new FileOutputStream(tmp)); 099 dos.writeLong(value); 100 dos.flush(); 101 102 } catch (IOException e) { 103 throw new WebcamException(e); 104 } finally { 105 if (dos != null) { 106 try { 107 dos.close(); 108 } catch (IOException e) { 109 throw new WebcamException(e); 110 } 111 } 112 } 113 114 if (!locked.get()) { 115 return; 116 } 117 118 if (!tmp.renameTo(lock)) { 119 LOG.warn("Ooops, system was not able to rename lock file from {} to {}", tmp, lock); 120 } 121 } 122 123 private long read() { 124 DataInputStream dis = null; 125 try { 126 return (dis = new DataInputStream(new FileInputStream(lock))).readLong(); 127 } catch (IOException e) { 128 throw new WebcamException(e); 129 } finally { 130 if (dis != null) { 131 try { 132 dis.close(); 133 } catch (IOException e) { 134 throw new WebcamException(e); 135 } 136 } 137 } 138 } 139 140 private void update() { 141 write(System.currentTimeMillis()); 142 } 143 144 /** 145 * Lock webcam. 146 */ 147 public void lock() { 148 149 if (isLocked()) { 150 throw new WebcamLockException(String.format("Webcam %s has already been locked", webcam.getName())); 151 } 152 153 if (!locked.compareAndSet(false, true)) { 154 return; 155 } 156 157 LOG.debug("Lock {}", webcam); 158 159 update(); 160 161 updater = new LockUpdater(); 162 updater.start(); 163 } 164 165 /** 166 * Unlock webcam. 167 */ 168 public void unlock() { 169 170 if (!locked.compareAndSet(true, false)) { 171 return; 172 } 173 174 LOG.debug("Unlock {}", webcam); 175 176 updater.interrupt(); 177 178 write(-1); 179 180 if (!lock.delete()) { 181 lock.deleteOnExit(); 182 } 183 } 184 185 /** 186 * Check if webcam is locked. 187 * 188 * @return True if webcam is locked, false otherwise 189 */ 190 public boolean isLocked() { 191 192 // check if locked by current process 193 194 if (locked.get()) { 195 return true; 196 } 197 198 // check if locked by other process 199 200 if (!lock.exists()) { 201 return false; 202 } 203 204 long now = System.currentTimeMillis(); 205 long tsp = read(); 206 207 LOG.trace("Lock timestamp {} now {} for {}", tsp, now, webcam); 208 209 if (tsp > now - INTERVAL * 2) { 210 return true; 211 } 212 213 return false; 214 } 215 }