从LiveSwitch示例代码中反编译,没有使用NativeWebcamDevice和NativeWebcamDriver(根据反编译结果,仅能在Windows下使用):
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package fm.liveswitch.java.sarxos; import com.github.sarxos.webcam.Webcam; import com.github.sarxos.webcam.WebcamEvent; import com.github.sarxos.webcam.WebcamListener; import fm.liveswitch.Future; import fm.liveswitch.Log; import fm.liveswitch.ManagedThread; import fm.liveswitch.Promise; import fm.liveswitch.SourceInput; import fm.liveswitch.VideoBuffer; import fm.liveswitch.VideoConfig; import fm.liveswitch.VideoFormat; import fm.liveswitch.VideoFrame; import fm.liveswitch.java.ImageUtility; import java.awt.Dimension; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.List; /** * * @author admin */ public class VideoSource extends fm.liveswitch.VideoSource implements WebcamListener { private VideoConfig config; private Webcam webcam = null; private volatile boolean isCapturing = false; private volatile boolean isStopped = true; private final Object threadLock = new Object(); private BufferedImage threadData = null; private long lastTimestamp; // 最近一次时间戳 public VideoSource(VideoConfig config) { super(VideoFormat.getRgb()); this.config = config; } public VideoConfig getConfig() { return this.config; } public void setConfig(VideoConfig videoConfig) { this.config = videoConfig; } @Override public Future<SourceInput[]> getInputs() { Promise<SourceInput[]> promise = new Promise(); List<SourceInput> inputs = new ArrayList(); for(Webcam wc : Webcam.getWebcams()) { inputs.add(new SourceInput(wc.getName(), wc.getName())); } promise.resolve(inputs.toArray(new SourceInput[inputs.size()])); return promise; } @Override protected Future<Object> doStart() { final Promise<Object> promise = new Promise(); final VideoSource self = this; ManagedThread.dispatch(() -> { try { VideoSource.this.webcam = null; SourceInput input = VideoSource.this.getInput(); if(input != null) { for(Webcam wc : Webcam.getWebcams()) { if(input.getId().equals(wc.getName())) { VideoSource.this.webcam = wc; } } } if(VideoSource.this.webcam == null) { VideoSource.this.webcam = Webcam.getDefault(); } VideoSource.this.setInput(new SourceInput(VideoSource.this.webcam.getName(), VideoSource.this.webcam.getName())); Dimension closestSize = null; int closestSizeDistance = -1; for(Dimension size : VideoSource.this.webcam.getViewSizes()) { int sizeDistance = self.getSizeDistance(VideoSource.this.config.getWidth(), VideoSource.this.config.getHeight(), size.width, size.height); if((closestSize == null) || (sizeDistance < closestSizeDistance)) { closestSize = size; closestSizeDistance = sizeDistance; } } if(closestSize != null) { VideoSource.this.webcam.setViewSize(closestSize); Log.info("Video capture: " + closestSize.width + "x" + closestSize.height + ", " + VideoSource.this.config.getFrameRate() + " fps"); } else { Log.info("Video capture: " + VideoSource.this.config.getFrameRate() + " fps"); } VideoSource.this.webcam.addWebcamListener(self); VideoSource.this.isCapturing = true; VideoSource.this.isStopped = false; // 启动线程用于捕获循环 ManagedThread thread = new ManagedThread((ManagedThread thread1) -> { VideoSource.this.captureLoop(thread1); }); thread.start(); // 检查摄像头是否打开,若摄像头被打开,则关闭 Log.debug("Checking if camera (" + VideoSource.this.getInput().getName() + ") is open."); if(VideoSource.this.webcam.isOpen()) { Log.debug("Closing camera (" + VideoSource.this.getInput().getName() + ")."); VideoSource.this.webcam.close(); } // 检查摄像头是否被锁定,如果摄像头被锁定,则等待释放 for(int i=0; i<10; i++) { Log.debug("Checking if camera (" + VideoSource.this.getInput().getName() + ") is locked."); if(!VideoSource.this.webcam.getLock().isLocked()) { break; } Log.debug(String.format("Waiting for camera (" + VideoSource.this.getInput().getName() + ") to be released (attempt #%d).", new Object[] {Integer.valueOf(i + 1)})); try { Thread.sleep(100L); } catch(Exception localException2) {} } if(VideoSource.this.webcam.getLock().isLocked()) { throw new RuntimeException("Camera (" + VideoSource.this.getInput().getName() + ") is locked."); } Log.debug("Opening camera."); VideoSource.this.webcam.open(true); Log.debug("Camera is ready."); promise.resolve(null); } catch(Exception ex) { Log.error("Error starting VideoSource.", ex); promise.reject(ex); } }); return promise; } @Override protected Future<Object> doStop() { final Promise<Object> promise = new Promise(); final VideoSource self = this; ManagedThread.dispatch(() -> { try { VideoSource.this.webcam.removeWebcamListener(self); VideoSource.this.isCapturing = false; while(!VideoSource.this.isStopped) { synchronized(VideoSource.this.threadLock) { VideoSource.this.threadLock.notify(); } try { ManagedThread.sleep(10); } catch(Exception localException) {} } if(VideoSource.this.webcam != null) { VideoSource.this.webcam.close(); VideoSource.this.webcam = null; } promise.resolve(null); } catch(Exception ex) { promise.reject(ex); } }); return promise; } @Override public String getLabel() { return "Sarxos Webcam Source"; } @Override public void webcamOpen(WebcamEvent we) { Log.debug("Webcam open"); } @Override public void webcamClosed(WebcamEvent we) { Log.debug("Webcam closed"); } @Override public void webcamDisposed(WebcamEvent we) { Log.debug("Webcam disposed"); } // 根据帧率(一秒钟多少帧)获得图像数据 // 获得图像数据期间加锁,图像数据获得完毕,通知等待线程 @Override public void webcamImageObtained(WebcamEvent we) { // 1纳秒是1秒的10亿分之1 int desiredFrameDurationInNanos = 1000000000 / config.getFrameRate(); long timestamp = System.nanoTime(); if((this.lastTimestamp != -1L) && (timestamp - this.lastTimestamp < desiredFrameDurationInNanos)) { return; } this.lastTimestamp = timestamp; synchronized(this.threadLock) { this.threadData = we.getImage(); this.threadLock.notify(); } } // 摄像头监听器负责按照帧率生成数据(webcamImageObtained),一旦准备好数据,通知captureLoop生成帧(raiseFrame) private void captureLoop(ManagedThread thread) { BufferedImage data = null; while(this.isCapturing) { if(data != null) { try { VideoBuffer buffer = ImageUtility.bufferedImageToBuffer(data); raiseFrame(new VideoFrame(buffer)); } catch(Exception ex) { Log.error("Could not raise frame from VideoSource", ex); } } synchronized(this.threadLock) { if(this.threadData == null) { try { this.threadLock.wait(); } catch(Exception localException) {} } data = this.threadData; this.threadData = null; } } this.isStopped = true; } }