前言

V4L2, linux下视频采集的事实标准, 通常的设备路径就是

/dev/video0
/dev/video1
....
/dev/videoX

其Java库,自然只能是JNI库 https://github.com/sarxos/v4l4j 这个库似乎已经停止维护,但v4l2的接口本身很稳定,没有更新的必要吧

添加maven/gradle依赖

不知道从哪个时间点开始,我本地的项目大多转成maven项目

<dependency>
    <groupId>com.github.sarxos</groupId>
    <artifactId>v4l4j</artifactId>
    <version>0.9.1-r507</version>
</dependency>
compile group: 'com.github.sarxos', name: 'v4l4j', version: '0.9.1-r507'

初始化本地库

v4l4j这个项目只提供了 linux x64和linux arm的二进制库, 其他系统需要自行编译了

so文件在v4l4j-0.9.1-r507.jar里面可以找到, 路径是 META-INF/native/linux-64

有两种加载方式,二选一就行.

// 标准做法
V4L4J.init();
// 不知名做法, 新版JDK支持指定路径, 貌似是JDK7开始的吧
System.load("/usr/local/lib/libvideo.so");
System.load("/usr/local/lib/libv4l4j.so");

开启设备,检查设备特性

基本流程, 创建设备,获取DeviceInfo, 检测是否兼容特定的特性.

很自然的,下面的代码当然用到了nutz相关的类

    private static final Log log = Logs.get();

    protected VideoDevice device;
    
    protected RGBFrameGrabber grabber;
    
    public void checkV4L4() {
        try {
            device = new VideoDevice("/dev/video0"); // 通常就是第0个设备
            DeviceInfo dinfo = device.getDeviceInfo();
            log.info("name=" + dinfo.getName());
            log.info("DeviceFile=" + dinfo.getDeviceFile());
            ImageFormatList ifl = dinfo.getFormatList();
            int w = 1920, h = 1080;
            if (ifl != null) {
                // 获取是原生支持的格式, 还有BGR/YVU/YUV等等,貌似我用不上,不打印了,反正触类旁通
                List<ImageFormat> natives = ifl.getNativeFormats();
                if (natives != null) {
                    for (ImageFormat format : natives) { 
                        // 理论上说支持多种分辨率,尤其是摄像头,但我这个采集卡只输出一种分辨率
                        log.info("native format = " + format.toNiceString());
                        w = format.getResolutionInfo().getDiscreteResolutions().get(0).width;
                        h = format.getResolutionInfo().getDiscreteResolutions().get(0).height;
                        log.infof("w=%d,h=%d", w, h);
                    }
                }
            }
            // 是否支持 BGR/JPEG/RGB/YUV/YVU转换,这些都非常重要, 最起码有一个会是true,通常是全部为true,取决是设备和linux内核版本.
            log.info("supportBGRConversion=" + device.supportBGRConversion());
            log.info("supportJPEGConversion=" + device.supportJPEGConversion());
            log.info("supportRGBConversion=" + device.supportRGBConversion());
            log.info("supportYUVConversion=" + device.supportYUVConversion());
            log.info("supportYVUConversion=" + device.supportYVUConversion());
            
            // 操作完成,释放设备. 但如果要开始捕捉,这一步必须去掉.
            device.release();
        }
        catch (V4L4JException e) {
            log.info("OhOhOh", e);
            device = null;
        }

通过v4l2-ctl获取更新信息

通过v4l4j的api能输出大部分必要的信息,但设备信息是拿不到的.

下面是执行v4l2-ctl –all输出的内容,中文注释是我手动加上的…

root@danoo-desktop:/dev/shm# v4l2-ctl --all
Driver Info (not using libv4l2):
        Driver name   : LINUXV4L2
        Card type     : PL330B:RAW 00.00 a0011af2
        Bus info      : PCIe: PCI Bus 0000:01
        Driver version: 3.12.0
        Capabilities  : 0x84221001
                Video Capture
                Video Capture Multiplanar
                Audio
                Streaming
                Device Capabilities
        Device Caps   : 0x04220001
                Video Capture
                Audio
                Streaming
Priority: 0
Video input : 3 (DVI-A INPUT(3): ok) ## 从0 - 7 代表各种接口. 其中3代表VGA(虽然名字里面有DVI)
Audio input : 0 () ## 木有音频输入.
Video Standard = 0x00001000
        NTSC-M     # PAL还是NTSC呢? 这里给出了答案
Format Video Capture:
        Width/Height  : 1920/1080 # 分辨率数据
        Pixel Format  : 'YV12'    # 原始像素格式
        Field         : Interlaced
        Bytes per Line: 1920
        Size Image    : 3110400
        Colorspace    : HDTV and modern devices (ITU709)
        Custom Info   : feedcafe
Crop Capability Video Capture:
        Bounds      : Left 0, Top 0, Width 1920, Height 1080
        Default     : Left 0, Top 0, Width 1920, Height 1080
        Pixel Aspect: 1/1
Streaming Parameters Video Capture:
        Capabilities     : timeperframe
        Frames per second: 60.000 (60000/1000) # 刷新率
        Read buffers     : 0
                     brightness (int)    : min=0 max=255 step=1 default=128 value=128
                       contrast (int)    : min=0 max=255 step=1 default=128 value=128
                     saturation (int)    : min=0 max=255 step=1 default=128 value=128
                            hue (int)    : min=0 max=255 step=1 default=128 value=128
                      auto_gain (int)    : min=0 max=1 step=1 default=1 value=1
                      sharpness (int)    : min=0 max=255 step=1 default=128 value=128

开始进行视频捕捉

            # 首先,需要确定哪种FrameGrabber可用(supportXXXConversion是否为true),然后选一种合适的. 对我来说就是RGBFrameGrabber
            # 然后,选择合适的输入设备, 我这里选3, VGA
            # 再然后, 选择NTSC, 取决于v4l2-ctl所显示的值. 我猜从DeviceInfo应该也能查到吧,没研究.
            grabber = device.getRGBFrameGrabber(w, h, 3, V4L4JConstants.STANDARD_NTSC);
            # 设置视频帧回调
            grabber.setCaptureCallback(new CaptureCallback() {
                public void nextFrame(VideoFrame frame) {
                    //log.info("frame income !!!" + frame.getFrameLength()); // 不晓得为啥,我这里总是0 ,但图片的确有了
                    try {
                        final BufferedImage image = frame.getBufferedImage();
                        # 做爱做的事吧... 如果是耗时操作,建议用线程池来执行,而不是在这个线程内完成
                    } catch (Throwable e) {
                        log.debug("BufferedImage FAIL..."  + e);
                    }
                    // 把当前帧回收,非常非常非常重要
                    frame.recycle();
                }
                public void exceptionReceived(V4L4JException e) {
                    log.info("something happen!!!", e);
                }
            });
            // 来吧, 开始趴体...
            grabber.startCapture();
            log.info("party start!!");
            // 趴体永不结束...
            Lang.quiteSleep(3600000);

性能

在我所使用的设备

"Intel(R) Celeron(R) CPU 1037U"
圆刚mini PICE采集卡

使用vga接口,采集1920x1080的彩色图像数据,能达到30fps左右, cpu占用率40%.

可能的原因:

  • 原生YV12数据,转为RGB数据需要一些计算量
  • 每帧图像需要8mb内存(BufferedImage),每秒30帧,该图像数据用完就扔, 相当于每秒产生240mb的可GC数据


blog comments powered by Disqus

Published

2017-06-20

Categories


Tags

Fork me on GitHub