1、效果图

2、补丁(参考https://github.com/jianfenggithub/V4L2_example

commit a3aab64424a416879be7651165e5b0b19da5400a
Author: rockemd <rockemd2020@163.com>
Date:   Wed Dec 9 23:10:26 2020 +0800

    support USB dual camera

diff --git a/app/QUVCTest/QUVCTest.pro b/app/QUVCTest/QUVCTest.pro
new file mode 100755
index 0000000..1b98345
--- /dev/null
+++ b/app/QUVCTest/QUVCTest.pro
@@ -0,0 +1,34 @@
+
+QT       += core gui
+
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
+
+CONFIG += c++11 console
+
+TARGET = QUVCTest
+TEMPLATE = app
+
+# The following define makes your compiler emit warnings if you use
+# any feature of Qt which as been marked deprecated (the exact warnings
+# depend on your compiler). Please consult the documentation of the
+# deprecated API in order to know how to port your code away from it.
+#DEFINES += QT_DEPRECATED_WARNINGS
+
+# You can also make your code fail to compile if you use deprecated APIs.
+# In order to do so, uncomment the following line.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
+
+SOURCES +=  \
+	main.cpp \
+	widget.cpp \
+    v4l2.cpp \
+    camerathread.cpp
+
+HEADERS  +=  \
+	widget.h \
+    v4l2.h \
+    camerathread.h
+
+FORMS    +=  \
+	widget.ui
diff --git a/app/QUVCTest/camerathread.cpp b/app/QUVCTest/camerathread.cpp
new file mode 100755
index 0000000..fc682bb
--- /dev/null
+++ b/app/QUVCTest/camerathread.cpp
@@ -0,0 +1,35 @@
+#include "camerathread.h"
+#include <QThread>
+
+extern QLabel *mylabel;
+extern QLabel *mylabel2;
+
+CameraThread::CameraThread()
+{
+
+}
+
+CameraThread::~CameraThread()
+{
+
+}
+
+/** 获取摄像头数据线程 */
+void CameraThread::dowork()
+{
+    av4l.V4l_Init("/dev/video44", 30);                            /** 初始化 */	
+	bv4l.V4l_Init("/dev/video43", 30);                            /** 初始化 */
+
+    while(1){
+        if((QThread::currentThread()->isInterruptionRequested())){/** 是否有中断此线程信号 */
+            av4l.Close_Camera();                                 /** 关闭摄像头 */
+			bv4l.Close_Camera();                                 /** 关闭摄像头 */
+            break;
+        }
+        currentimagea = av4l.Get_image();                         /** 获得一帧图片数据 */
+		currentimageb = bv4l.Get_image();                         /** 获得一帧图片数据 */
+        mylabel->setPixmap(QPixmap::fromImage(currentimagea));   /** 显示到label上 */
+		mylabel2->setPixmap(QPixmap::fromImage(currentimageb));   /** 显示到label上 */
+    }
+
+}
diff --git a/app/QUVCTest/camerathread.h b/app/QUVCTest/camerathread.h
new file mode 100755
index 0000000..48414e2
--- /dev/null
+++ b/app/QUVCTest/camerathread.h
@@ -0,0 +1,27 @@
+#ifndef CAMERATHREAD_H
+#define CAMERATHREAD_H
+
+#include <QObject>
+#include "v4l2.h"
+#include <QImage>
+#include "widget.h"
+#include "ui_widget.h"
+
+class CameraThread : public QObject
+{
+    Q_OBJECT
+public:
+    CameraThread();
+    ~CameraThread();
+    V4L2 av4l;
+	V4L2 bv4l;
+    QImage currentimagea;            /** 保存摄像头一帧图片数据,然后显示到label上 */
+	QImage currentimageb;            /** 保存摄像头一帧图片数据,然后显示到label上 */
+
+signals:
+
+public slots:
+    void dowork();                  /** 获取摄像头数据线程 */
+};
+
+#endif // CAMERATHREAD_H
diff --git a/app/QUVCTest/main.cpp b/app/QUVCTest/main.cpp
new file mode 100755
index 0000000..f92e588
--- /dev/null
+++ b/app/QUVCTest/main.cpp
@@ -0,0 +1,26 @@
+#include "widget.h"
+#include <QApplication>
+#include <QThread>
+#include "camerathread.h"
+
+QThread *ImageThread = new QThread;                         /** 获取摄像头数据线程 */
+
+int main(int argc, char *argv[])
+{
+    QApplication a(argc, argv);
+    Widget w;
+
+    CameraThread *camera_worker = new CameraThread;
+
+    camera_worker->moveToThread(ImageThread);               /** 创建线程 */
+    QObject::connect(ImageThread, SIGNAL(started()),
+                     camera_worker, SLOT(dowork()));
+    QObject::connect(ImageThread, SIGNAL(finished()),
+                     camera_worker, SLOT(deleteLater()));   /** 当线程结束时,释放资源 */
+
+    ImageThread->start();                                   /** 启动线程 */
+
+    w.show();
+
+    return a.exec();
+}
diff --git a/app/QUVCTest/v4l2.cpp b/app/QUVCTest/v4l2.cpp
new file mode 100755
index 0000000..15e49f7
--- /dev/null
+++ b/app/QUVCTest/v4l2.cpp
@@ -0,0 +1,155 @@
+/**
+ * 功能:使用V4L2采集UVC摄像头数据
+ * 日期:2018.3.26
+ * 用法:在Ubuntu下的Qt工程中添加V4L2.h和V4L2.cpp文件,定义一个V4L2对象,
+ *      调用bool V4L2::V4l_Init(char* camera_path, unsigned int frame) 函数对摄像头进行初始化操作
+ *      调用QImage V4L2::Get_image() 函数就可以获得摄像头传来的一张图片
+ *      调用bool Close_Camera(void)函数关闭摄像头
+ * 注意:需要在V4L2.cpp的54行左右可修改摄像头图片的输出格式,如修改为MJPEG输出
+ *      在V4L2.h的宏定义中可以修改输出图片的像素
+ *
+ * v1.1-2018.9.26:修改注释风格,规范代码
+ * v1.2-2019.3.2:优化Get_image函数,减少数据转移次数
+ */
+
+#include "v4l2.h"
+
+V4L2::V4L2()
+{
+    n = 0;
+}
+
+V4L2::~V4L2()
+{
+
+}
+
+/** 摄像头初始化,需要传入摄像头的挂载路径和输出的帧率,初始化成功则返回真 */
+bool V4L2::V4l_Init(char *camera_path, unsigned int frame)
+{
+    if((fd=open(camera_path, O_RDWR)) == -1){          /** 读写方式打开摄像头 */
+        qDebug()<<"Error opening V4L interface";
+        return false;
+    }
+    if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1){       /** 检查摄像头的信息 */
+        qDebug()<<"Error opening device "<<camera_path<<": unable to query device.";
+        return false;
+    }else{                                             /** 打印摄像头的信息 */
+        qDebug()<<"driver:\t\t"<<QString::fromLatin1((char *)cap.driver);     /** 驱动名 */
+        qDebug()<<"card:\t\t"<<QString::fromLatin1((char *)cap.card);         /** Device名 */
+        qDebug()<<"bus_info:\t\t"<<QString::fromLatin1((char *)cap.bus_info); /** 在Bus系统中存放位置 */
+        qDebug()<<"version:\t\t"<<cap.version;                                /** driver 版本 */
+        qDebug()<<"capabilities:\t"<<cap.capabilities;                        /** 能力集,通常为:V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING */
+    }
+
+    fmtdesc.index=0;
+    fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;          /** 和struct v4l2_format中的type一致 */
+    qDebug()<<"Support format:";
+    while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1){     /** 获取摄像头输出图片所支持的格式 */
+        qDebug()<<"\t\t"<<fmtdesc.index+1<<QString::fromLatin1((char *)fmtdesc.description);
+        fmtdesc.index++;
+    }
+
+    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;            /** 像素格式 */
+    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;       /** JPEG格式输出 */
+    fmt.fmt.pix.height = Image_High;                   /** 图像尺寸,在这里设置想要输出的图片宽和高 */
+    fmt.fmt.pix.width = Image_Width;
+    fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;         /** 视频帧传输方式,交错式 */
+    if(ioctl(fd, VIDIOC_S_FMT, &fmt) == -1){           /** 配置摄像头的采集格式 */
+        qDebug()<<"Unable to set format";
+        return false;
+    }
+    if(ioctl(fd, VIDIOC_G_FMT, &fmt) == -1){           /** 重新读取结构体,以确认完成设置 */
+        qDebug()<<"Unable to get format";
+        return false;
+    }else{
+        qDebug()<<"fmt.type:\t\t"<<fmt.type;            /** 输出像素格式 */
+        qDebug()<<"pix.height:\t"<<fmt.fmt.pix.height;/** 输出图像的尺寸 */
+        qDebug()<<"pix.width:\t\t"<<fmt.fmt.pix.width;
+        qDebug()<<"pix.field:\t\t"<<fmt.fmt.pix.field;  /** 视频帧传输方式 */
+    }
+
+    setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    setfps.parm.capture.timeperframe.denominator = frame;/** 预期的帧率 */
+    setfps.parm.capture.timeperframe.numerator = 1;     /** fps=frame/1 */
+    if(ioctl(fd, VIDIOC_S_PARM, &setfps)==-1){          /** 设置帧数 */
+        qDebug()<<"Unable to set fps";
+        return false;
+    }
+    if(ioctl(fd, VIDIOC_G_PARM, &setfps)==-1){          /** 重新读取结构体,以确认完成设置 */
+        qDebug()<<"Unable to get fps";
+        return false;
+    }else{
+        qDebug()<<"fps:\t\t"<<setfps.parm.capture.timeperframe.denominator/setfps.parm.capture.timeperframe.numerator;/** 输出帧率 */
+    }
+
+    req.count=Video_count;                              /** 3个缓存区 */
+    req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;               /** 与struct v4l2_format中的type一致 */
+    req.memory=V4L2_MEMORY_MMAP;                        /** Memory Mapping模式,设置为V4L2_MEMORY_MMAP时count字段才有效 */
+    if(ioctl(fd,VIDIOC_REQBUFS,&req)==-1){              /** 向内核申请视频缓存 */
+        qDebug()<<"request for buffers error";
+        return false;
+    }
+    for (i=0; i<Video_count; i++){                      /** mmap四个缓冲区 */
+        bzero(&buffer[i], sizeof(buffer[i]));
+        buffer[i].type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+        buffer[i].memory = V4L2_MEMORY_MMAP;
+        buffer[i].index = i;
+        if (ioctl (fd, VIDIOC_QUERYBUF, &buffer[i]) == -1){ /** 获得缓冲区的参数,mmap时需要这些参数 */
+            qDebug()<<"query buffer error";
+            return false;
+        }
+        length[i] = buffer[i].length;                   /** 保存缓存的大小 */
+        start[i] = (unsigned char *)mmap(NULL,buffer[i].length,PROT_READ |PROT_WRITE, MAP_SHARED, fd, buffer[i].m.offset);
+                                                        /** 将申请的内核缓存映射到用户空间 */
+    }
+    for (i=0; i<Video_count; i++){
+        buffer[i].index = i;
+        ioctl(fd, VIDIOC_QBUF, &buffer[i]);             /** 将缓存入队 */
+    }
+    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    ioctl (fd, VIDIOC_STREAMON, &type);                 /** 开启I/O流 */
+
+    bzero(&v4lbuf, sizeof(v4lbuf));
+    v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    v4lbuf.memory = V4L2_MEMORY_MMAP;
+    qDebug()<<"init ok";
+
+    return true;
+}
+
+QImage V4L2::Get_image()                               /** 得到摄像头捕获的图片,返回值为QImage类型的变量 */
+{
+    v4lbuf.index = n%Video_count;
+    ioctl(fd, VIDIOC_DQBUF, &v4lbuf);                  /** 将获得图像数据的缓存出队 */
+
+    QByteArray temp;
+    temp.append((const char *)start[n%Video_count],length[n%Video_count]);
+    image.loadFromData(temp);                          /** 将图片数据放入image变量中 */
+
+    v4lbuf.index = n%Video_count;
+    ioctl(fd, VIDIOC_QBUF, &v4lbuf);                   /** 将已经读取过数据的缓存块重新入队 */
+    n++;
+    if(n == 3){                                        /** 防止n累加溢出 */
+        n = 0;
+    }
+    return image;                                      /** 返回图片数据 */
+}
+
+bool V4L2::Close_Camera()                              /** 关闭摄像头,关闭成功则返回真 */
+{
+    if(fd != -1){
+        ioctl(fd, VIDIOC_STREAMOFF, &type);            /** 结束图像显示 */
+        int n = close(fd);                             /** 关闭视频设备 */
+        if(n == -1){
+            qDebug()<<"close camera failed";
+            return false;
+        }
+    }
+    for(i=0; i<Video_count; i++){
+        if(start[i] != NULL){                          /** 释放申请的内存 */
+            start[i] = NULL;
+        }
+    }
+    return true;
+}
diff --git a/app/QUVCTest/v4l2.h b/app/QUVCTest/v4l2.h
new file mode 100755
index 0000000..b417ea3
--- /dev/null
+++ b/app/QUVCTest/v4l2.h
@@ -0,0 +1,57 @@
+/**
+ * 功能:使用V4L2采集UVC摄像头数据
+ * 日期:2018.3.26
+ * 用法:在Ubuntu下的Qt工程中添加V4L2.h和V4L2.cpp文件,定义一个V4L2对象,
+ *      调用bool V4L2::V4l_Init(char* camera_path, unsigned int frame) 函数对摄像头进行初始化操作
+ *      调用QImage V4L2::Get_image() 函数就可以获得摄像头传来的一张图片
+ *      调用bool Close_Camera(void)函数关闭摄像头
+ * 注意:需要在V4L2.cpp的54行左右可修改摄像头图片的输出格式,如修改为MJPEG输出
+ *      在V4L2.h的宏定义中可以修改输出图片的像素
+ *
+ * v1.1-2018.9.26:修改注释风格,规范代码
+ * v1.2-2019.3.2:优化Get_image函数,减少数据转移次数
+ */
+
+#ifndef V4L2_H
+#define V4L2_H
+
+#include <fcntl.h>                                  /** 打开摄像头的open函数所在的头文件 */
+#include <sys/mman.h>                               /** 将申请的内核缓存映射到用户空间的mmap函数所在头文件 */
+#include <linux/videodev2.h>                        /** V4L2相关结构体所在的头文件 */
+#include <unistd.h>                                 /** 关闭摄像头的close函数所在的头文件 */
+#include <sys/ioctl.h>
+#include <QDebug>
+#include <QImage>
+
+#define Video_count    3                            /** 缓冲帧数 */
+#define Image_Width    1280                         /** 输出图片的像素 */
+#define Image_High     720
+
+class V4L2
+{
+public:
+    V4L2();
+    ~V4L2();
+    bool V4l_Init(char *camera_path, unsigned int frame);/** 摄像头初始化函数,需要传入摄像头的挂载路径和输出的帧率,初始化成功则返回真 */
+    QImage Get_image(void);                         /** 获取图片,返回值为QImage类型的变量 */
+    bool Close_Camera(void);                        /** 关闭摄像头,关闭成功则返回真 */
+    int n;                                          /** 用在Get_image函数,获取图片时,用来控制哪快缓存 */
+
+private:
+    int                           i;
+    int                           fd;               /** 摄像头句柄 */
+    int                           length[Video_count];/** 用来保存申请的缓存的大小 */
+    QImage                        image;
+    unsigned char *               start[Video_count];/** 用来保存图片数据 */
+
+    struct v4l2_buffer            buffer[Video_count];/** 向内核申请缓存时用到此结构体,每一个struct v4l2_buffer对应内核摄像头驱动中的一个缓存 */
+    struct v4l2_format            fmt;               /** 用来设置像素格式 图片输出格式 图像尺寸 扫描方式 */
+    struct v4l2_fmtdesc           fmtdesc;           /** 获取摄像头输出图片所支持的格式时需要用到此结构体 */
+    struct v4l2_capability        cap;               /** 检查摄像头的信息时需要用到此结构体 */
+    struct v4l2_streamparm        setfps;            /** 设置帧率时用到此结构体 */
+    struct v4l2_requestbuffers    req;               /** 向内核申请缓存时用到此结构体 */
+    struct v4l2_buffer            v4lbuf;            /** 缓存出队 入队时用到此结构体 */
+    enum   v4l2_buf_type          type;              /** 开启I/O流时用到此结构体 */
+};
+
+#endif // V4L2_H
diff --git a/app/QUVCTest/widget.cpp b/app/QUVCTest/widget.cpp
new file mode 100755
index 0000000..00bf7c3
--- /dev/null
+++ b/app/QUVCTest/widget.cpp
@@ -0,0 +1,30 @@
+#include "widget.h"
+#include "ui_widget.h"
+
+
+QLabel *mylabel = NULL;
+QLabel *mylabel2 = NULL;
+extern QThread *ImageThread;    /** 获取摄像头数据线程 */
+
+Widget::Widget(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::Widget)
+{
+    ui->setupUi(this);
+
+    mylabel = ui->label;        /** 获得界面上的label对象, 使得可以在“获取摄像头数据线程”获取的图片数据显示到这个label上 */
+	mylabel2 = ui->label2;        /** 获得界面上的label对象, 使得可以在“获取摄像头数据线程”获取的图片数据显示到这个label上 */
+}
+
+Widget::~Widget()
+{
+    delete ui;
+}
+
+/** 关闭窗口时候的处理函数 */
+void Widget::closeEvent(QCloseEvent *)
+{
+    ImageThread->requestInterruption(); /** 请求中断 */
+    ImageThread->quit();                /** 关闭线程 */
+    ImageThread->wait();                /** 同步关闭 */
+}
diff --git a/app/QUVCTest/widget.h b/app/QUVCTest/widget.h
new file mode 100755
index 0000000..9852ecc
--- /dev/null
+++ b/app/QUVCTest/widget.h
@@ -0,0 +1,25 @@
+#ifndef WIDGET_H
+#define WIDGET_H
+
+#include <QWidget>
+#include <QThread>
+
+namespace Ui {
+class Widget;
+}
+
+class Widget : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit Widget(QWidget *parent = 0);
+    ~Widget();
+    Ui::Widget *ui;
+    void closeEvent(QCloseEvent *);                 /** 关闭窗口时候的处理函数 */
+
+private:
+
+};
+
+#endif // WIDGET_H
diff --git a/app/QUVCTest/widget.ui b/app/QUVCTest/widget.ui
new file mode 100755
index 0000000..81c6246
--- /dev/null
+++ b/app/QUVCTest/widget.ui
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Widget</class>
+ <widget class="QWidget" name="Widget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>480</width>
+    <height>800</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Widget</string>
+  </property>
+  <widget class="QLabel" name="label">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>480</width>
+     <height>400</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>picture</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label2">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>400</y>
+     <width>480</width>
+     <height>400</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>picture</string>
+   </property>
+  </widget>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/buildroot/configs/rockchip_rv1126_rv1109_facial_gate_defconfig b/buildroot/configs/rockchip_rv1126_rv1109_facial_gate_defconfig
old mode 100644
new mode 100755
index 71e9632..55c7442
--- a/buildroot/configs/rockchip_rv1126_rv1109_facial_gate_defconfig
+++ b/buildroot/configs/rockchip_rv1126_rv1109_facial_gate_defconfig
@@ -24,6 +24,7 @@ BR2_PACKAGE_RKMEDIA_ANR=y
 BR2_PACKAGE_RKMEDIA_AEC=y
 BR2_PACKAGE_RKMEDIA_EXAMPLES=y
 BR2_PACKAGE_QFACIALGATE=y
+BR2_PACKAGE_QUVCTEST=y
 # BR2_PACKAGE_MINIGUI_SOFTWARE_SCALE is not set
 BR2_PACKAGE_RKNPU_USE_MINI_DRIVER=y
 BR2_PACKAGE_RKFACIAL_USE_WEB_SERVER=y
diff --git a/buildroot/package/rockchip/Config.in b/buildroot/package/rockchip/Config.in
old mode 100644
new mode 100755
index 6735cdc..5f381b9
--- a/buildroot/package/rockchip/Config.in
+++ b/buildroot/package/rockchip/Config.in
@@ -180,6 +180,7 @@ source "package/rockchip/carmachine/Config.in"
 source "package/rockchip/gallery/Config.in"
 source "package/rockchip/QLauncher/Config.in"
 source "package/rockchip/QFacialGate/Config.in"
+source "package/rockchip/QUVCTest/Config.in"
 source "package/rockchip/settings/Config.in"
 source "package/rockchip/qcamera/Config.in"
 source "package/rockchip/qfm/Config.in"
diff --git a/buildroot/package/rockchip/QUVCTest/Config.in b/buildroot/package/rockchip/QUVCTest/Config.in
new file mode 100755
index 0000000..45cb896
--- /dev/null
+++ b/buildroot/package/rockchip/QUVCTest/Config.in
@@ -0,0 +1,4 @@
+config BR2_PACKAGE_QUVCTEST
+	bool "quvctest"
+	help
+		rockemd qt quvctest
\ No newline at end of file
diff --git a/buildroot/package/rockchip/QUVCTest/QUVCTest.mk b/buildroot/package/rockchip/QUVCTest/QUVCTest.mk
new file mode 100755
index 0000000..26d2a86
--- /dev/null
+++ b/buildroot/package/rockchip/QUVCTest/QUVCTest.mk
@@ -0,0 +1,23 @@
+################################################################################
+#
+# QUVCTest
+#
+################################################################################
+
+QUVCTEST_VERSION = 1.0
+QUVCTEST_SITE = $(TOPDIR)/../app/QUVCTest
+QUVCTEST_SITE_METHOD = local
+
+QUVCTEST_LICENSE = Apache V2.0
+QUVCTEST_LICENSE_FILES = NOTICE
+define QUVCTEST_CONFIGURE_CMDS
+cd $(@D); $(TARGET_MAKE_ENV) $(HOST_DIR)/bin/qmake 
+endef
+define QUVCTEST_BUILD_CMDS
+$(TARGET_MAKE_ENV) $(MAKE) -C $(@D) 
+endef
+
+define QUVCTEST_INSTALL_TARGET_CMDS
+$(INSTALL) -D -m 0755 $(@D)/QUVCTest $(TARGET_DIR)/usr/bin/QUVCTest
+endef
+$(eval $(generic-package))
\ No newline at end of file

技术交流群: 微信号

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐