使用webcam-capture实现的VideoSource

Posted cuizhenfu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用webcam-capture实现的VideoSource相关的知识,希望对你有一定的参考价值。

从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;
    }
}

 

以上是关于使用webcam-capture实现的VideoSource的主要内容,如果未能解决你的问题,请参考以下文章

Youtube Data API V3 - 使用 google.youtube.videos.list() 获取视频时出错

《Unsupervised Learning of Object Structure and Dynamics from Videos》翻译理解

MYSQL 按顺序排列

Youtube Data API V3 - 使用google.youtube.videos.list()获取视频时出错

html embeding-的YouTube,videos.html

css 使用iFrame的响应式YouTube和Vimeo视频 - 来自https://www.ostraining.com/blog/coding/responsive-videos/