* [PATCH 0/4] OpenGL render for qv4l2
@ 2013-07-16 11:24 Bård Eirik Winther
2013-07-16 11:24 ` [PATCH 1/4] qv4l2: move function ctrlEvent Bård Eirik Winther
0 siblings, 1 reply; 9+ messages in thread
From: Bård Eirik Winther @ 2013-07-16 11:24 UTC (permalink / raw)
To: linux-media; +Cc: hansverk
qv4l2:
The qv4l2 test utility now supports OpenGL-accelerated display of video.
This allows for using the graphics card to render the video content to screen
and to performing color space conversion.
The OpenGL implementation requires the QtOpenGL library as well as an OpenGL driver.
If OpenGL support is not present, then the program will fall back to using the CPU to display.
Some of the changes/improvements:
- Moved the ctrlEvent() function in qv4l2.cpp to be grouped with GUI function
and to group capFrame() and capVbiFrame() together.
- OpenGL acceleration for supported systems.
- Option to change between GPU or CPU based rendering.
- CaptureWin's setMinimumSize() sets the minimum size for the video frame viewport
and not for the window itself. If the minimum size is larger than the monitor resolution,
it will reduce the minimum size to match this.
- Added a new menu option 'Preview' for controlling the CaptureWin's behavior.
- Added a couple of hotkeys:
CTRL + V : When main window is selected start capture.
This gives an option other than the button to start recording,
as this is a frequent operation when using the utility.
CTRL + W : When CaptureWin is selected close capture window.
It makes it easier to deal with high resolutions video on
small screen, especially when the window close button may
be outside the monitor when repositioning the window.
Known issues:
- Repositioning, scaling or switching windows while the capture is recording will reduce the framerate.
This is a limitation of Qt and not OpenGL.
- Using 4 streams of RGB3 1080p60 can at random times cease to render correctly
and reduce the framerate to half. A restart solves this though.
- OpenGL is limited to 60fps. Disabling V-sync might allow for faster framerates.
- Resizing or scaling is not supported, mainly because the YUY2 shader renders the image incorrectly
when the canvas size is not equal to the frame size.
Supported formats for OpenGL render:
- Native supported and accelerated:
V4L2_PIX_FMT_RGB32
V4L2_PIX_FMT_BGR32
V4L2_PIX_FMT_RGB24
V4L2_PIX_FMT_BGR24
V4L2_PIX_FMT_RGB565
V4L2_PIX_FMT_RGB555
V4L2_PIX_FMT_YUYV
V4L2_PIX_FMT_YVYU
V4L2_PIX_FMT_UYVY
V4L2_PIX_FMT_VYUY
V4L2_PIX_FMT_YVU420
V4L2_PIX_FMT_YUV420
- All formats supported by V4L conversion library,
but they will use the CPU to convert to RGB3 before being displayed with OpenGL.
Performance:
All tests are done on an Intel i7-2600S (with Turbo Boost disabled) using the
integrated Intel HD 2000 graphics processor. The mothreboard is an ASUS P8H77-I
with 2x2GB CL 9-9-9-24 DDR3 RAM. The capture card is a Cisco test card with 4 HDMI
inputs connected using PCIe2.0x8. All video input streams used for testing are
progressive HD (1920x1080) with 60fps.
FPS for every input for a given number of streams:
1 STREAM 2 STREAMS 3 STREAMS 4 STREAMS
RGB3 60 60 60 60
BGR3 60 60 60 50
YUYV 60 60 60 48
YU12 60 60 60 52
YV12 60 60 60 52
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/4] qv4l2: move function ctrlEvent
2013-07-16 11:24 [PATCH 0/4] OpenGL render for qv4l2 Bård Eirik Winther
@ 2013-07-16 11:24 ` Bård Eirik Winther
2013-07-16 11:24 ` [PATCH 2/4] qv4l2: add hotkeys for common operations Bård Eirik Winther
` (2 more replies)
0 siblings, 3 replies; 9+ messages in thread
From: Bård Eirik Winther @ 2013-07-16 11:24 UTC (permalink / raw)
To: linux-media; +Cc: hansverk
Moved the ctrlEvent() function in qv4l2.cpp to be grouped with GUI function
and to group capFrame() and capVbiFrame() together.
Signed-off-by: Bård Eirik Winther <bwinther@cisco.com>
---
utils/qv4l2/qv4l2.cpp | 94 +++++++++++++++++++++++++--------------------------
1 file changed, 47 insertions(+), 47 deletions(-)
diff --git a/utils/qv4l2/qv4l2.cpp b/utils/qv4l2/qv4l2.cpp
index de9b154..a8fcc65 100644
--- a/utils/qv4l2/qv4l2.cpp
+++ b/utils/qv4l2/qv4l2.cpp
@@ -202,6 +202,53 @@ void ApplicationWindow::openrawdev()
setDevice(d.selectedFiles().first(), true);
}
+void ApplicationWindow::ctrlEvent()
+{
+ v4l2_event ev;
+
+ while (dqevent(ev)) {
+ if (ev.type != V4L2_EVENT_CTRL)
+ continue;
+ m_ctrlMap[ev.id].flags = ev.u.ctrl.flags;
+ m_ctrlMap[ev.id].minimum = ev.u.ctrl.minimum;
+ m_ctrlMap[ev.id].maximum = ev.u.ctrl.maximum;
+ m_ctrlMap[ev.id].step = ev.u.ctrl.step;
+ m_ctrlMap[ev.id].default_value = ev.u.ctrl.default_value;
+ m_widgetMap[ev.id]->setDisabled(m_ctrlMap[ev.id].flags & CTRL_FLAG_DISABLED);
+ switch (m_ctrlMap[ev.id].type) {
+ case V4L2_CTRL_TYPE_INTEGER:
+ case V4L2_CTRL_TYPE_INTEGER_MENU:
+ case V4L2_CTRL_TYPE_MENU:
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ case V4L2_CTRL_TYPE_BITMASK:
+ setVal(ev.id, ev.u.ctrl.value);
+ break;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ setVal64(ev.id, ev.u.ctrl.value64);
+ break;
+ default:
+ break;
+ }
+ if (m_ctrlMap[ev.id].type != V4L2_CTRL_TYPE_STRING)
+ continue;
+ queryctrl(m_ctrlMap[ev.id]);
+
+ struct v4l2_ext_control c;
+ struct v4l2_ext_controls ctrls;
+
+ c.id = ev.id;
+ c.size = m_ctrlMap[ev.id].maximum + 1;
+ c.string = (char *)malloc(c.size);
+ memset(&ctrls, 0, sizeof(ctrls));
+ ctrls.count = 1;
+ ctrls.ctrl_class = 0;
+ ctrls.controls = &c;
+ if (!ioctl(VIDIOC_G_EXT_CTRLS, &ctrls))
+ setString(ev.id, c.string);
+ free(c.string);
+ }
+}
+
void ApplicationWindow::capVbiFrame()
{
__u32 buftype = m_genTab->bufType();
@@ -305,53 +352,6 @@ void ApplicationWindow::capVbiFrame()
refresh();
}
-void ApplicationWindow::ctrlEvent()
-{
- v4l2_event ev;
-
- while (dqevent(ev)) {
- if (ev.type != V4L2_EVENT_CTRL)
- continue;
- m_ctrlMap[ev.id].flags = ev.u.ctrl.flags;
- m_ctrlMap[ev.id].minimum = ev.u.ctrl.minimum;
- m_ctrlMap[ev.id].maximum = ev.u.ctrl.maximum;
- m_ctrlMap[ev.id].step = ev.u.ctrl.step;
- m_ctrlMap[ev.id].default_value = ev.u.ctrl.default_value;
- m_widgetMap[ev.id]->setDisabled(m_ctrlMap[ev.id].flags & CTRL_FLAG_DISABLED);
- switch (m_ctrlMap[ev.id].type) {
- case V4L2_CTRL_TYPE_INTEGER:
- case V4L2_CTRL_TYPE_INTEGER_MENU:
- case V4L2_CTRL_TYPE_MENU:
- case V4L2_CTRL_TYPE_BOOLEAN:
- case V4L2_CTRL_TYPE_BITMASK:
- setVal(ev.id, ev.u.ctrl.value);
- break;
- case V4L2_CTRL_TYPE_INTEGER64:
- setVal64(ev.id, ev.u.ctrl.value64);
- break;
- default:
- break;
- }
- if (m_ctrlMap[ev.id].type != V4L2_CTRL_TYPE_STRING)
- continue;
- queryctrl(m_ctrlMap[ev.id]);
-
- struct v4l2_ext_control c;
- struct v4l2_ext_controls ctrls;
-
- c.id = ev.id;
- c.size = m_ctrlMap[ev.id].maximum + 1;
- c.string = (char *)malloc(c.size);
- memset(&ctrls, 0, sizeof(ctrls));
- ctrls.count = 1;
- ctrls.ctrl_class = 0;
- ctrls.controls = &c;
- if (!ioctl(VIDIOC_G_EXT_CTRLS, &ctrls))
- setString(ev.id, c.string);
- free(c.string);
- }
-}
-
void ApplicationWindow::capFrame()
{
__u32 buftype = m_genTab->bufType();
--
1.8.3.2
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 2/4] qv4l2: add hotkeys for common operations
2013-07-16 11:24 ` [PATCH 1/4] qv4l2: move function ctrlEvent Bård Eirik Winther
@ 2013-07-16 11:24 ` Bård Eirik Winther
2013-07-16 11:24 ` [PATCH 3/4] qv4l2: fix CaptureWin mimimum size to match video fram size Bård Eirik Winther
2013-07-16 11:24 ` [PATCH 4/4] qv4l2: add OpenGL video render Bård Eirik Winther
2 siblings, 0 replies; 9+ messages in thread
From: Bård Eirik Winther @ 2013-07-16 11:24 UTC (permalink / raw)
To: linux-media; +Cc: hansverk
CTRL + V : When main window is selected start capture.
This gives an option other than the button to start recording,
as this is a frequent operation when using the utility.
CTRL + W : When CaptureWin is selected close capture window
It makes it easier to deal with high resolutions video on
small screen, especially when the window close button may
be outside the monitor when repositioning the window.
Signed-off-by: Bård Eirik Winther <bwinther@cisco.com>
---
utils/qv4l2/capture-win.cpp | 8 ++++++++
utils/qv4l2/capture-win.h | 4 +++-
utils/qv4l2/qv4l2.cpp | 1 +
3 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/utils/qv4l2/capture-win.cpp b/utils/qv4l2/capture-win.cpp
index 6798252..a94c73d 100644
--- a/utils/qv4l2/capture-win.cpp
+++ b/utils/qv4l2/capture-win.cpp
@@ -35,6 +35,14 @@ CaptureWin::CaptureWin()
vbox->addWidget(m_label);
vbox->addWidget(m_msg);
+
+ hotkeyClose = new QShortcut(Qt::CTRL+Qt::Key_W, this);
+ QObject::connect(hotkeyClose, SIGNAL(activated()), this, SLOT(close()));
+}
+
+CaptureWin::~CaptureWin()
+{
+ delete hotkeyClose;
}
void CaptureWin::setImage(const QImage &image, const QString &status)
diff --git a/utils/qv4l2/capture-win.h b/utils/qv4l2/capture-win.h
index e861b12..4115d56 100644
--- a/utils/qv4l2/capture-win.h
+++ b/utils/qv4l2/capture-win.h
@@ -21,6 +21,7 @@
#define CAPTURE_WIN_H
#include <QWidget>
+#include <QShortcut>
#include <sys/time.h>
class QImage;
@@ -32,7 +33,7 @@ class CaptureWin : public QWidget
public:
CaptureWin();
- virtual ~CaptureWin() {}
+ ~CaptureWin();
void setImage(const QImage &image, const QString &status);
@@ -45,6 +46,7 @@ signals:
private:
QLabel *m_label;
QLabel *m_msg;
+ QShortcut *hotkeyClose;
};
#endif
diff --git a/utils/qv4l2/qv4l2.cpp b/utils/qv4l2/qv4l2.cpp
index a8fcc65..bb1d84f 100644
--- a/utils/qv4l2/qv4l2.cpp
+++ b/utils/qv4l2/qv4l2.cpp
@@ -78,6 +78,7 @@ ApplicationWindow::ApplicationWindow() :
m_capStartAct->setStatusTip("Start capturing");
m_capStartAct->setCheckable(true);
m_capStartAct->setDisabled(true);
+ m_capStartAct->setShortcut(Qt::CTRL+Qt::Key_V);
connect(m_capStartAct, SIGNAL(toggled(bool)), this, SLOT(capStart(bool)));
m_snapshotAct = new QAction(QIcon(":/snapshot.png"), "&Make Snapshot", this);
--
1.8.3.2
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 3/4] qv4l2: fix CaptureWin mimimum size to match video fram size
2013-07-16 11:24 ` [PATCH 1/4] qv4l2: move function ctrlEvent Bård Eirik Winther
2013-07-16 11:24 ` [PATCH 2/4] qv4l2: add hotkeys for common operations Bård Eirik Winther
@ 2013-07-16 11:24 ` Bård Eirik Winther
2013-07-16 11:24 ` [PATCH 4/4] qv4l2: add OpenGL video render Bård Eirik Winther
2 siblings, 0 replies; 9+ messages in thread
From: Bård Eirik Winther @ 2013-07-16 11:24 UTC (permalink / raw)
To: linux-media; +Cc: hansverk
CaptureWin's setMinimumSize() sets the minimum size for the
video frame viewport and not for the window itself.
If the minimum size is larger than the monitor resolution,
it will reduce the minimum size to match this.
Signed-off-by: Bård Eirik Winther <bwinther@cisco.com>
---
utils/qv4l2/capture-win.cpp | 26 ++++++++++++++++++++++++++
utils/qv4l2/capture-win.h | 1 +
2 files changed, 27 insertions(+)
diff --git a/utils/qv4l2/capture-win.cpp b/utils/qv4l2/capture-win.cpp
index a94c73d..7ac3fa1 100644
--- a/utils/qv4l2/capture-win.cpp
+++ b/utils/qv4l2/capture-win.cpp
@@ -21,6 +21,8 @@
#include <QImage>
#include <QVBoxLayout>
#include <QCloseEvent>
+#include <QApplication>
+#include <QDesktopWidget>
#include "qv4l2.h"
#include "capture-win.h"
@@ -36,6 +38,10 @@ CaptureWin::CaptureWin()
vbox->addWidget(m_label);
vbox->addWidget(m_msg);
+ int l, t, r, b;
+ vbox->getContentsMargins(&l, &t, &r, &b);
+ vbox->setSpacing(b);
+
hotkeyClose = new QShortcut(Qt::CTRL+Qt::Key_W, this);
QObject::connect(hotkeyClose, SIGNAL(activated()), this, SLOT(close()));
}
@@ -45,6 +51,26 @@ CaptureWin::~CaptureWin()
delete hotkeyClose;
}
+void CaptureWin::setMinimumSize(int minw, int minh)
+{
+ QDesktopWidget *screen = QApplication::desktop();
+ QRect resolution = screen->screenGeometry();
+
+ int l, t, r, b;
+ layout()->getContentsMargins(&l, &t, &r, &b);
+ minw += l + r;
+ minh += t + b + m_msg->minimumSizeHint().height() + layout()->spacing();
+
+ if (minw > resolution.width())
+ minw = resolution.width();
+
+ if (minh > resolution.height())
+ minh = resolution.height();
+
+ QWidget::setMinimumSize(minw, minh);
+ resize(minw, minh);
+}
+
void CaptureWin::setImage(const QImage &image, const QString &status)
{
m_label->setPixmap(QPixmap::fromImage(image));
diff --git a/utils/qv4l2/capture-win.h b/utils/qv4l2/capture-win.h
index 4115d56..3de3447 100644
--- a/utils/qv4l2/capture-win.h
+++ b/utils/qv4l2/capture-win.h
@@ -35,6 +35,7 @@ public:
CaptureWin();
~CaptureWin();
+ virtual void setMinimumSize(int minw, int minh);
void setImage(const QImage &image, const QString &status);
protected:
--
1.8.3.2
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 4/4] qv4l2: add OpenGL video render
2013-07-16 11:24 ` [PATCH 1/4] qv4l2: move function ctrlEvent Bård Eirik Winther
2013-07-16 11:24 ` [PATCH 2/4] qv4l2: add hotkeys for common operations Bård Eirik Winther
2013-07-16 11:24 ` [PATCH 3/4] qv4l2: fix CaptureWin mimimum size to match video fram size Bård Eirik Winther
@ 2013-07-16 11:24 ` Bård Eirik Winther
2013-07-16 12:01 ` Laurent Pinchart
2 siblings, 1 reply; 9+ messages in thread
From: Bård Eirik Winther @ 2013-07-16 11:24 UTC (permalink / raw)
To: linux-media; +Cc: hansverk
The qv4l2 test utility now supports OpenGL-accelerated display of video.
This allows for using the graphics card to render the video content to screen
and to performing color space conversion.
Signed-off-by: Bård Eirik Winther <bwinther@cisco.com>
---
configure.ac | 8 +-
utils/qv4l2/Makefile.am | 9 +-
utils/qv4l2/capture-win.cpp | 559 ++++++++++++++++++++++++++++++++++++++++++--
utils/qv4l2/capture-win.h | 81 ++++++-
utils/qv4l2/qv4l2.cpp | 173 +++++++++++---
utils/qv4l2/qv4l2.h | 8 +
6 files changed, 782 insertions(+), 56 deletions(-)
diff --git a/configure.ac b/configure.ac
index e249546..97a0dfc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -128,7 +128,12 @@ if test "x$qt_pkgconfig" = "xtrue"; then
AC_SUBST(UIC)
AC_SUBST(RCC)
else
- AC_MSG_WARN(Qt4 is not available)
+ AC_MSG_WARN(Qt4 or higher is not available)
+fi
+
+PKG_CHECK_MODULES(QTOGL, [QtOpenGL >= 4.4 gl], [qt_pkgconfig_ogl=true], [qt_pkgconfig_ogl=false])
+if test "x$qt_pkgconfig_ogl" = "xfalse"; then
+ AC_MSG_WARN(Qt4 OpenGL or higher is not available)
fi
AC_SUBST([JPEG_LIBS])
@@ -237,6 +242,7 @@ AM_CONDITIONAL([WITH_LIBDVBV5], [test x$enable_libdvbv5 = xyes])
AM_CONDITIONAL([WITH_LIBV4L], [test x$enable_libv4l != xno])
AM_CONDITIONAL([WITH_V4LUTILS], [test x$enable_v4lutils != xno])
AM_CONDITIONAL([WITH_QV4L2], [test ${qt_pkgconfig} = true -a x$enable_qv4l2 != xno])
+AM_CONDITIONAL([WITH_QV4L2_OGL_COMPILE], [test WITH_QV4L2 -a ${qt_pkgconfig_ogl} = true])
AM_CONDITIONAL([WITH_V4L_PLUGINS], [test x$enable_libv4l != xno -a x$enable_shared != xno])
AM_CONDITIONAL([WITH_V4L_WRAPPERS], [test x$enable_libv4l != xno -a x$enable_shared != xno])
diff --git a/utils/qv4l2/Makefile.am b/utils/qv4l2/Makefile.am
index 1f5a49f..86bf786 100644
--- a/utils/qv4l2/Makefile.am
+++ b/utils/qv4l2/Makefile.am
@@ -4,8 +4,14 @@ qv4l2_SOURCES = qv4l2.cpp general-tab.cpp ctrl-tab.cpp vbi-tab.cpp v4l2-api.cpp
raw2sliced.cpp qv4l2.h capture-win.h general-tab.h vbi-tab.h v4l2-api.h raw2sliced.h
nodist_qv4l2_SOURCES = moc_qv4l2.cpp moc_general-tab.cpp moc_capture-win.cpp moc_vbi-tab.cpp qrc_qv4l2.cpp
qv4l2_LDADD = ../../lib/libv4l2/libv4l2.la ../../lib/libv4lconvert/libv4lconvert.la ../libv4l2util/libv4l2util.la
+
+if WITH_QV4L2_OGL_COMPILE
+qv4l2_CPPFLAGS = $(QTOGL_CFLAGS) -DENABLE_OGL
+qv4l2_LDFLAGS = $(QTOGL_LIBS)
+else
qv4l2_CPPFLAGS = $(QT_CFLAGS)
qv4l2_LDFLAGS = $(QT_LIBS)
+endif
EXTRA_DIST = exit.png fileopen.png qv4l2_24x24.png qv4l2_64x64.png qv4l2.png qv4l2.svg snapshot.png \
video-television.png fileclose.png qv4l2_16x16.png qv4l2_32x32.png qv4l2.desktop qv4l2.qrc record.png \
@@ -37,5 +43,4 @@ install-data-local:
$(INSTALL_DATA) -D -p "$(srcdir)/qv4l2_24x24.png" "$(DESTDIR)$(datadir)/icons/hicolor/24x24/apps/qv4l2.png"
$(INSTALL_DATA) -D -p "$(srcdir)/qv4l2_32x32.png" "$(DESTDIR)$(datadir)/icons/hicolor/32x32/apps/qv4l2.png"
$(INSTALL_DATA) -D -p "$(srcdir)/qv4l2_64x64.png" "$(DESTDIR)$(datadir)/icons/hicolor/64x64/apps/qv4l2.png"
- $(INSTALL_DATA) -D -p "$(srcdir)/qv4l2.svg" "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps/qv4l2.svg"
-
+ $(INSTALL_DATA) -D -p "$(srcdir)/qv4l2.svg" "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps/qv4l2.svg"
\ No newline at end of file
diff --git a/utils/qv4l2/capture-win.cpp b/utils/qv4l2/capture-win.cpp
index 7ac3fa1..132d23d 100644
--- a/utils/qv4l2/capture-win.cpp
+++ b/utils/qv4l2/capture-win.cpp
@@ -16,6 +16,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
#include <stdio.h>
#include <QLabel>
#include <QImage>
@@ -24,23 +25,12 @@
#include <QApplication>
#include <QDesktopWidget>
-#include "qv4l2.h"
#include "capture-win.h"
-CaptureWin::CaptureWin()
+CaptureWin::CaptureWin(bool useOGL)
{
- QVBoxLayout *vbox = new QVBoxLayout(this);
-
- setWindowTitle("V4L2 Capture");
- m_label = new QLabel();
- m_msg = new QLabel("No frame");
-
- vbox->addWidget(m_label);
- vbox->addWidget(m_msg);
-
- int l, t, r, b;
- vbox->getContentsMargins(&l, &t, &r, &b);
- vbox->setSpacing(b);
+ m_enableOGL = useOGL;
+ buildCanvas();
hotkeyClose = new QShortcut(Qt::CTRL+Qt::Key_W, this);
QObject::connect(hotkeyClose, SIGNAL(activated()), this, SLOT(close()));
@@ -49,6 +39,48 @@ CaptureWin::CaptureWin()
CaptureWin::~CaptureWin()
{
delete hotkeyClose;
+ clearCanvas();
+}
+
+void CaptureWin::clearCanvas()
+{
+ if (layout() != NULL) {
+ if (m_enableOGL) {
+#ifdef ENABLE_OGL
+ delete m_canvas;
+#endif
+ } else {
+ delete m_videoSurface;
+ }
+
+ delete m_information;
+ layout()->removeWidget(this);
+ delete layout();
+
+ }
+}
+
+void CaptureWin::buildCanvas()
+{
+ QVBoxLayout *vbox = new QVBoxLayout(this);
+
+ if (m_enableOGL) {
+ setWindowTitle("V4L2 Preview (OpenGL)");
+#if ENABLE_OGL
+ m_canvas = new CaptureCanvas();
+ vbox->addWidget(m_canvas, 2000);
+#endif
+ } else {
+ setWindowTitle("V4L2 Preview");
+ m_videoSurface = new QLabel();
+ vbox->addWidget(m_videoSurface, 2000);
+ }
+
+ int l, t, r, b;
+ m_information = new QLabel("No Frame");
+ vbox->addWidget(m_information, 1, Qt::AlignBottom);
+ vbox->getContentsMargins(&l, &t, &r, &b);
+ vbox->setSpacing(b);
}
void CaptureWin::setMinimumSize(int minw, int minh)
@@ -59,7 +91,7 @@ void CaptureWin::setMinimumSize(int minw, int minh)
int l, t, r, b;
layout()->getContentsMargins(&l, &t, &r, &b);
minw += l + r;
- minh += t + b + m_msg->minimumSizeHint().height() + layout()->spacing();
+ minh += t + b + m_information->minimumSizeHint().height() + layout()->spacing();
if (minw > resolution.width())
minw = resolution.width();
@@ -71,10 +103,43 @@ void CaptureWin::setMinimumSize(int minw, int minh)
resize(minw, minh);
}
-void CaptureWin::setImage(const QImage &image, const QString &status)
+void CaptureWin::setFrameCPU(const QImage &image, const QString &status)
+{
+ m_videoSurface->setPixmap(QPixmap::fromImage(image));
+ m_information->setText(status);
+}
+
+void CaptureWin::setFrameOGL(int width, int height, __u32 format,
+ unsigned char *data, const QString &status)
+{
+#if ENABLE_OGL
+ m_canvas->setFrame(width, height, format, data);
+#endif
+ m_information->setText(status);
+}
+
+void CaptureWin::stop()
+{
+#if ENABLE_OGL
+ if (m_enableOGL)
+ m_canvas->stop();
+#endif
+}
+
+void CaptureWin::setEnableOGL(bool enable)
+{
+#ifdef ENABLE_OGL
+ if (m_enableOGL != enable) {
+ clearCanvas();
+ m_enableOGL = enable;
+ buildCanvas();
+ }
+#endif
+}
+
+bool CaptureWin::isEnableOGL()
{
- m_label->setPixmap(QPixmap::fromImage(image));
- m_msg->setText(status);
+ return m_enableOGL;
}
void CaptureWin::closeEvent(QCloseEvent *event)
@@ -82,3 +147,461 @@ void CaptureWin::closeEvent(QCloseEvent *event)
QWidget::closeEvent(event);
emit close();
}
+
+#if ENABLE_OGL
+CaptureCanvas::CaptureCanvas() :
+ m_frameHeight(0),
+ m_frameWidth(0),
+ m_screenTextureCount(0),
+ m_formatChange(false),
+ m_frameFormat(0),
+ m_frameData(NULL)
+{
+ m_glfunction.initializeGLFunctions(context());
+}
+
+CaptureCanvas::~CaptureCanvas()
+{
+ clearShader();
+}
+
+void CaptureCanvas::clearShader()
+{
+ glDeleteTextures(m_screenTextureCount, m_screenTexture);
+ m_shaderProgram.release();
+ m_shaderProgram.removeAllShaders();
+}
+
+void CaptureCanvas::stop()
+{
+ // Setting the m_frameData to NULL stops OpenGL
+ // from updating frames on repaint
+ m_frameData = NULL;
+}
+
+void CaptureCanvas::initializeGL()
+{
+ glShadeModel(GL_FLAT);
+ glEnable(GL_TEXTURE_2D);
+
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ checkError("InitializeGL");
+}
+
+
+void CaptureCanvas::resizeGL(int width, int height)
+{
+ // Resizing is disabled by setting viewport equal to frame size
+ glViewport(0, 0, m_frameWidth, m_frameHeight);
+}
+
+void CaptureCanvas::setFrame(int width, int height, __u32 format, unsigned char *data)
+{
+ if (format != m_frameFormat || width != m_frameWidth || height != m_frameHeight) {
+ m_formatChange = true;
+ m_frameWidth = width;
+ m_frameHeight = height;
+ m_frameFormat = format;
+
+ setMaximumSize(m_frameWidth, m_frameHeight);
+ showFullScreen();
+ }
+
+ m_frameData = data;
+ updateGL();
+}
+
+void CaptureCanvas::checkError(const char *msg)
+{
+ int err = glGetError();
+ if (err) fprintf(stderr, "OpenGL Error 0x%x: %s.\n", err, msg);
+}
+
+void CaptureCanvas::changeShader()
+{
+ m_formatChange = false;
+ clearShader();
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, m_frameWidth, m_frameHeight, 0, 0, 1);
+ resizeGL(width(), height());
+ checkError("Render settings.\n");
+
+ switch (m_frameFormat) {
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ shader_YUY2(m_frameFormat);
+ break;
+
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ shader_YUV();
+ break;
+
+ case V4L2_PIX_FMT_RGB32:
+ m_screenTextureCount = 1;
+ glActiveTexture(GL_TEXTURE0);
+ glGenTextures(m_screenTextureCount, m_screenTexture);
+ configureTexture(0);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA4, m_frameWidth, m_frameHeight, 0,
+ GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, NULL);
+ checkError("RGB32 shader");
+ break;
+
+ case V4L2_PIX_FMT_BGR32:
+ m_screenTextureCount = 1;
+ glActiveTexture(GL_TEXTURE0);
+ glGenTextures(m_screenTextureCount, m_screenTexture);
+ configureTexture(0);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA4, m_frameWidth, m_frameHeight, 0,
+ GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, NULL);
+ checkError("BGR32 shader");
+ break;
+
+ case V4L2_PIX_FMT_RGB555:
+ m_screenTextureCount = 1;
+ glActiveTexture(GL_TEXTURE0);
+ glGenTextures(m_screenTextureCount, m_screenTexture);
+ configureTexture(0);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, m_frameWidth, m_frameHeight, 0,
+ GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, NULL);
+ checkError("RGB555 shader");
+ break;
+
+ case V4L2_PIX_FMT_RGB565:
+ m_screenTextureCount = 1;
+ glActiveTexture(GL_TEXTURE0);
+ glGenTextures(m_screenTextureCount, m_screenTexture);
+ configureTexture(0);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_frameWidth, m_frameHeight, 0,
+ GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL);
+ checkError("RGB565 shader");
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ shader_BGR();
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ default:
+ m_screenTextureCount = 1;
+ glActiveTexture(GL_TEXTURE0);
+ glGenTextures(m_screenTextureCount, m_screenTexture);
+ configureTexture(0);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_frameWidth, m_frameHeight, 0,
+ GL_RGB, GL_UNSIGNED_BYTE, NULL);
+ checkError("Default shader");
+ break;
+ }
+}
+
+void CaptureCanvas::paintGL()
+{
+ if (m_frameWidth < 1 || m_frameHeight < 1) {
+ glClear(GL_COLOR_BUFFER_BIT);
+ return;
+ }
+
+ if (m_formatChange)
+ changeShader();
+
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ switch (m_frameFormat) {
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ render_YUY2();
+ break;
+
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ render_YUV(m_frameFormat);
+ break;
+
+ case V4L2_PIX_FMT_RGB32:
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_frameWidth, m_frameHeight,
+ GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, m_frameData);
+ checkError("RGB32 paint");
+ break;
+
+ case V4L2_PIX_FMT_BGR32:
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_frameWidth, m_frameHeight,
+ GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, m_frameData);
+ checkError("BGR32 paint");
+ break;
+
+ case V4L2_PIX_FMT_RGB555:
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_frameWidth, m_frameHeight,
+ GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, m_frameData);
+ checkError("RGB555 paint");
+ break;
+
+ case V4L2_PIX_FMT_RGB565:
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_frameWidth, m_frameHeight,
+ GL_RGB, GL_UNSIGNED_SHORT_5_6_5, m_frameData);
+ checkError("RGB565 paint");
+ break;
+
+ case V4L2_PIX_FMT_BGR24:
+ render_BGR();
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ default:
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, m_screenTexture[0]);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_frameWidth, m_frameHeight,
+ GL_RGB, GL_UNSIGNED_BYTE, m_frameData);
+ checkError("Default paint");
+ break;
+ }
+
+ glBegin(GL_QUADS);
+ glTexCoord2f(0.0f, 0.0f); glVertex2f(0.0, 0);
+ glTexCoord2f(1.0f, 0.0f); glVertex2f(m_frameWidth, 0);
+ glTexCoord2f(1.0f, 1.0f); glVertex2f(m_frameWidth, m_frameHeight);
+ glTexCoord2f(0.0f, 1.0f); glVertex2f(0, m_frameHeight);
+ glEnd();
+}
+
+void CaptureCanvas::configureTexture(size_t idx)
+{
+ glBindTexture(GL_TEXTURE_2D, m_screenTexture[idx]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+}
+
+void CaptureCanvas::shader_YUV()
+{
+ m_screenTextureCount = 3;
+ glGenTextures(m_screenTextureCount, m_screenTexture);
+
+ glActiveTexture(GL_TEXTURE0);
+ configureTexture(0);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_frameWidth, m_frameHeight, 0,
+ GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL);
+ checkError("YUV shader texture 0");
+
+ glActiveTexture(GL_TEXTURE1);
+ configureTexture(1);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_frameWidth / 2, m_frameHeight / 2, 0,
+ GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL);
+ checkError("YUV shader texture 1");
+
+ glActiveTexture(GL_TEXTURE2);
+ configureTexture(2);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_frameWidth / 2, m_frameHeight / 2, 0,
+ GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL);
+ checkError("YUV shader texture 2");
+
+ bool src_c = m_shaderProgram.addShaderFromSourceCode(
+ QGLShader::Fragment,
+ "uniform sampler2D ytex;"
+ "uniform sampler2D utex;"
+ "uniform sampler2D vtex;"
+ "void main()"
+ "{"
+ " vec2 xy = vec2(gl_TexCoord[0].xy);"
+ " float y = 1.1640625 * (texture2D(ytex, xy).r - 0.0625);"
+ " float u = texture2D(utex, xy).r - 0.5;"
+ " float v = texture2D(vtex, xy).r - 0.5;"
+ " float r = y + 1.59765625 * v;"
+ " float g = y - 0.390625 * u - 0.8125 *v;"
+ " float b = y + 2.015625 * u;"
+ " gl_FragColor = vec4(r, g, b, 1.0);"
+ "}"
+ );
+
+ if (!src_c)
+ fprintf(stderr, "OpenGL Error: YUV shader compilation failed.\n");
+
+ m_shaderProgram.bind();
+}
+
+void CaptureCanvas::render_YUV(__u32 format)
+{
+ int idxU;
+ int idxV;
+ if (format == V4L2_PIX_FMT_YUV420) {
+ idxU = m_frameWidth * m_frameHeight;
+ idxV = idxU + (idxU / 4);
+ } else {
+ idxV = m_frameWidth * m_frameHeight;
+ idxU = idxV + (idxV / 4);
+ }
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, m_screenTexture[0]);
+ GLint Y = m_glfunction.glGetUniformLocation(m_shaderProgram.programId(), "ytex");
+ glUniform1i(Y, 0);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_frameWidth, m_frameHeight,
+ GL_LUMINANCE, GL_UNSIGNED_BYTE, m_frameData);
+ checkError("YUV paint ytex");
+
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, m_screenTexture[1]);
+ GLint U = m_glfunction.glGetUniformLocation(m_shaderProgram.programId(), "utex");
+ glUniform1i(U, 1);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_frameWidth / 2, m_frameHeight / 2,
+ GL_LUMINANCE, GL_UNSIGNED_BYTE, m_frameData == NULL ? NULL : &m_frameData[idxU]);
+ checkError("YUV paint utex");
+
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, m_screenTexture[2]);
+ GLint V = m_glfunction.glGetUniformLocation(m_shaderProgram.programId(), "vtex");
+ glUniform1i(V, 2);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_frameWidth / 2, m_frameHeight / 2,
+ GL_LUMINANCE, GL_UNSIGNED_BYTE, m_frameData == NULL ? NULL : &m_frameData[idxV]);
+ checkError("YUV paint vtex");
+}
+
+void CaptureCanvas::shader_BGR()
+{
+ m_screenTextureCount = 1;
+ glGenTextures(m_screenTextureCount, m_screenTexture);
+ configureTexture(0);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_frameWidth, m_frameHeight, 0,
+ GL_RGB, GL_UNSIGNED_BYTE, NULL);
+ checkError("BGR shader");
+
+ bool src_c = m_shaderProgram.addShaderFromSourceCode(
+ QGLShader::Fragment,
+ "uniform sampler2D tex;"
+ "void main()"
+ "{"
+ " vec4 color = texture2D(tex, gl_TexCoord[0].xy);"
+ " gl_FragColor = vec4(color.b, color.g, color.r, 1.0);"
+ "}"
+ );
+ if (!src_c)
+ fprintf(stderr, "OpenGL Error: BGR shader compilation failed.\n");
+
+ m_shaderProgram.bind();
+}
+
+void CaptureCanvas::render_BGR()
+{
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, m_screenTexture[0]);
+ GLint Y = m_glfunction.glGetUniformLocation(m_shaderProgram.programId(), "tex");
+ glUniform1i(Y, 0);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_frameWidth, m_frameHeight,
+ GL_RGB, GL_UNSIGNED_BYTE, m_frameData);
+ checkError("BGR paint");
+}
+
+QString CaptureCanvas::shader_YUY2_invariant(__u32 format)
+{
+ switch (format) {
+ case V4L2_PIX_FMT_YUYV:
+ return QString("y = (luma_chroma.r - 0.0625) * 1.1643;"
+ "if (mod(xcoord, 2.0) == 0.0) {"
+ " u = luma_chroma.a;"
+ " v = texture2D(tex, vec2(pixelx + texl_w, pixely)).a;"
+ "} else {"
+ " v = luma_chroma.a;"
+ " u = texture2D(tex, vec2(pixelx - texl_w, pixely)).a;"
+ "}"
+ );
+
+ case V4L2_PIX_FMT_YVYU:
+ return QString("y = (luma_chroma.r - 0.0625) * 1.1643;"
+ "if (mod(xcoord, 2.0) == 0.0) {"
+ " v = luma_chroma.a;"
+ " u = texture2D(tex, vec2(pixelx + texl_w, pixely)).a;"
+ "} else {"
+ " u = luma_chroma.a;"
+ " v = texture2D(tex, vec2(pixelx - texl_w, pixely)).a;"
+ "}"
+ );
+
+ case V4L2_PIX_FMT_UYVY:
+ return QString("y = (luma_chroma.a - 0.0625) * 1.1643;"
+ "if (mod(xcoord, 2.0) == 0.0) {"
+ " u = luma_chroma.r;"
+ " v = texture2D(tex, vec2(pixelx + texl_w, pixely)).r;"
+ "} else {"
+ " v = luma_chroma.r;"
+ " u = texture2D(tex, vec2(pixelx - texl_w, pixely)).r;"
+ "}"
+ );
+
+ case V4L2_PIX_FMT_VYUY:
+ return QString("y = (luma_chroma.a - 0.0625) * 1.1643;"
+ "if (mod(xcoord, 2.0) == 0.0) {"
+ " v = luma_chroma.r;"
+ " u = texture2D(tex, vec2(pixelx + texl_w, pixely)).r;"
+ "} else {"
+ " u = luma_chroma.r;"
+ " v = texture2D(tex, vec2(pixelx - texl_w, pixely)).r;"
+ "}"
+ );
+
+ default:
+ return QString();
+ }
+}
+
+void CaptureCanvas::shader_YUY2(__u32 format)
+{
+ m_screenTextureCount = 1;
+ glGenTextures(m_screenTextureCount, m_screenTexture);
+ configureTexture(0);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, m_frameWidth, m_frameHeight, 0,
+ GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL);
+ checkError("YUY2 shader");
+
+ QString codeHead = QString("uniform sampler2D tex;"
+ "uniform float texl_w;"
+ "uniform float tex_w;"
+ "void main()"
+ "{"
+ " float y, u, v;"
+ " float pixelx = gl_TexCoord[0].x;"
+ " float pixely = gl_TexCoord[0].y;"
+ " float xcoord = floor(pixelx * tex_w);"
+ " vec4 luma_chroma = texture2D(tex, vec2(pixelx, pixely));"
+ );
+
+ QString codeBody = shader_YUY2_invariant(format);
+
+ QString codeTail = QString(" u = u - 0.5;"
+ " v = v - 0.5;"
+ " float r = y + 1.5958 * v;"
+ " float g = y - 0.39173 * u - 0.81290 * v;"
+ " float b = y + 2.017 * u;"
+ " gl_FragColor = vec4(r, g, b, 1.0);"
+ "}"
+ );
+
+ bool src_ok = m_shaderProgram.addShaderFromSourceCode(
+ QGLShader::Fragment, QString("%1%2%3").arg(codeHead, codeBody, codeTail)
+ );
+
+ if (!src_ok)
+ fprintf(stderr, "OpenGL Error: YUY2 shader compilation failed.\n");
+
+ m_shaderProgram.bind();
+}
+
+void CaptureCanvas::render_YUY2()
+{
+ int idx;
+ idx = glGetUniformLocation(m_shaderProgram.programId(), "texl_w"); // Texel width
+ glUniform1f(idx, 1.0 / m_frameWidth);
+ idx = glGetUniformLocation(m_shaderProgram.programId(), "tex_w"); // Texture width
+ glUniform1f(idx, m_frameWidth);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, m_screenTexture[0]);
+ GLint Y = m_glfunction.glGetUniformLocation(m_shaderProgram.programId(), "tex");
+ glUniform1i(Y, 0);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_frameWidth, m_frameHeight,
+ GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, m_frameData);
+ checkError("YUY2 paint");
+}
+#endif
diff --git a/utils/qv4l2/capture-win.h b/utils/qv4l2/capture-win.h
index 3de3447..4d6f04c 100644
--- a/utils/qv4l2/capture-win.h
+++ b/utils/qv4l2/capture-win.h
@@ -24,19 +24,83 @@
#include <QShortcut>
#include <sys/time.h>
+#ifdef ENABLE_OGL
+#define GL_GLEXT_PROTOTYPES
+#include <QGLWidget>
+#include <QGLShader>
+#include <QGLShaderProgram>
+#include <QGLFunctions>
+#endif
+
+#include "qv4l2.h"
+
class QImage;
class QLabel;
+#ifdef ENABLE_OGL
+class CaptureCanvas : public QGLWidget
+{
+public:
+ CaptureCanvas();
+ ~CaptureCanvas();
+
+ void setFrame(int width, int height, __u32 format, unsigned char *bufptr);
+ void stop();
+
+protected:
+ void paintGL();
+ void initializeGL();
+ void resizeGL(int width, int height);
+
+private:
+ // Colorspace conversion shaders
+ void shader_YUV();
+ void shader_BGR();
+ void shader_YUY2(__u32 format);
+ QString shader_YUY2_invariant(__u32 format);
+
+ // Colorspace conversion render
+ void render_BGR();
+ void render_YUY2();
+ void render_YUV(__u32 format);
+
+
+ void clearShader();
+ void changeShader();
+ void configureTexture(size_t idx);
+ void checkError(const char *msg);
+
+ int m_frameHeight;
+ int m_frameWidth;
+ int m_screenTextureCount;
+ bool m_formatChange;
+ __u32 m_frameFormat;
+ GLuint m_screenTexture[3]; // This must be equal to the max number of textures that any shader uses
+ QGLFunctions m_glfunction;
+ unsigned char *m_frameData;
+ QGLShaderProgram m_shaderProgram;
+
+};
+#endif /* OGL_COMPILE */
+
+
+
+
class CaptureWin : public QWidget
{
Q_OBJECT
public:
- CaptureWin();
+ CaptureWin(bool useOGL);
~CaptureWin();
virtual void setMinimumSize(int minw, int minh);
- void setImage(const QImage &image, const QString &status);
+ void setFrameCPU(const QImage &image, const QString &status);
+ void setFrameOGL(int width, int height, __u32 format,
+ unsigned char *data, const QString &status);
+ void setEnableOGL(bool enable);
+ bool isEnableOGL();
+ void stop();
protected:
virtual void closeEvent(QCloseEvent *event);
@@ -45,9 +109,16 @@ signals:
void close();
private:
- QLabel *m_label;
- QLabel *m_msg;
+ void buildCanvas();
+ void clearCanvas();
+
+ bool m_enableOGL;
+ QLabel *m_videoSurface;
+ QLabel *m_information;
QShortcut *hotkeyClose;
+#if ENABLE_OGL
+ CaptureCanvas *m_canvas;
+#endif
};
-#endif
+#endif /* CAPTURE_WIN_H */
diff --git a/utils/qv4l2/qv4l2.cpp b/utils/qv4l2/qv4l2.cpp
index bb1d84f..22f93f1 100644
--- a/utils/qv4l2/qv4l2.cpp
+++ b/utils/qv4l2/qv4l2.cpp
@@ -107,6 +107,15 @@ ApplicationWindow::ApplicationWindow() :
quitAct->setShortcut(Qt::CTRL+Qt::Key_Q);
connect(quitAct, SIGNAL(triggered()), this, SLOT(close()));
+#ifdef ENABLE_OGL
+ m_previewAct = new QAction("Use Open&GL", this);
+ m_previewAct->setStatusTip("Use the GPU for video capture preview if set.");
+ m_previewAct->setCheckable(true);
+ m_previewAct->setChecked(true);
+ m_previewAct->setEnabled(true);
+ connect(m_previewAct, SIGNAL(triggered()), this, SLOT(setPreviewMode()));
+#endif
+
QMenu *fileMenu = menuBar()->addMenu("&File");
fileMenu->addAction(openAct);
fileMenu->addAction(openRawAct);
@@ -114,7 +123,6 @@ ApplicationWindow::ApplicationWindow() :
fileMenu->addAction(m_capStartAct);
fileMenu->addAction(m_snapshotAct);
fileMenu->addAction(m_saveRawAct);
- fileMenu->addAction(m_showFramesAct);
fileMenu->addSeparator();
fileMenu->addAction(quitAct);
@@ -128,6 +136,13 @@ ApplicationWindow::ApplicationWindow() :
toolBar->addSeparator();
toolBar->addAction(quitAct);
+ QMenu *previewMenu = menuBar()->addMenu("&Preview");
+ previewMenu->addAction(m_showFramesAct);
+#ifdef ENABLE_OGL
+ previewMenu->addSeparator();
+ previewMenu->addAction(m_previewAct);
+#endif
+
QMenu *helpMenu = menuBar()->addMenu("&Help");
helpMenu->addAction("&About", this, SLOT(about()), Qt::Key_F1);
@@ -151,6 +166,15 @@ ApplicationWindow::~ApplicationWindow()
void ApplicationWindow::setDevice(const QString &device, bool rawOpen)
{
+ bool useOGL;
+#ifdef ENABLE_OGL
+ useOGL = true;
+#else
+ useOGL = false;
+#endif
+ if (m_capture != NULL)
+ useOGL = m_capture->isEnableOGL();
+
closeDevice();
m_sigMapper = new QSignalMapper(this);
connect(m_sigMapper, SIGNAL(mapped(int)), this, SLOT(ctrlAction(int)));
@@ -158,9 +182,12 @@ void ApplicationWindow::setDevice(const QString &device, bool rawOpen)
if (!open(device, !rawOpen))
return;
- m_capture = new CaptureWin;
+ m_capture = new CaptureWin(useOGL);
m_capture->setMinimumSize(150, 50);
connect(m_capture, SIGNAL(close()), this, SLOT(closeCaptureWin()));
+#ifdef ENABLE_OGL
+ m_previewAct->setChecked(useOGL);
+#endif
QWidget *w = new QWidget(m_tabs);
m_genTab = new GeneralTab(device, *this, 4, w);
@@ -203,6 +230,18 @@ void ApplicationWindow::openrawdev()
setDevice(d.selectedFiles().first(), true);
}
+void ApplicationWindow::setPreviewMode()
+{
+ if (m_capStartAct->isChecked()) {
+#ifdef ENABLE_OGL
+ m_previewAct->setChecked(m_capture->isEnableOGL());
+#endif
+ return;
+ }
+
+ m_capture->setEnableOGL(m_previewAct->isChecked());
+}
+
void ApplicationWindow::ctrlEvent()
{
v4l2_event ev;
@@ -344,8 +383,15 @@ void ApplicationWindow::capVbiFrame()
m_tv = tv;
}
status = QString("Frame: %1 Fps: %2").arg(++m_frame).arg(m_fps);
- if (m_showFrames)
- m_capture->setImage(*m_capImage, status);
+ if (m_showFrames) {
+ if (m_capture->isEnableOGL())
+ m_capture->setFrameOGL(m_capImage->width(), m_capImage->height(),
+ m_capSrcFormat.fmt.pix.pixelformat, m_capImage->bits(), "No frame");
+ else
+ m_capture->setFrameCPU(*m_capImage, "No frame");
+
+ m_capture->setFrameCPU(*m_capImage, status);
+ }
curStatus = statusBar()->currentMessage();
if (curStatus.isEmpty() || curStatus.startsWith("Frame: "))
statusBar()->showMessage(status);
@@ -361,6 +407,9 @@ void ApplicationWindow::capFrame()
int err = 0;
bool again;
+ unsigned char *displaybuf = NULL;
+ bool ogl_use = m_capture->isEnableOGL();
+
switch (m_capMethod) {
case methodRead:
s = read(m_frameData, m_capSrcFormat.fmt.pix.sizeimage);
@@ -380,9 +429,13 @@ void ApplicationWindow::capFrame()
break;
if (m_mustConvert)
err = v4lconvert_convert(m_convertData, &m_capSrcFormat, &m_capDestFormat,
- m_frameData, s,
- m_capImage->bits(), m_capDestFormat.fmt.pix.sizeimage);
- if (!m_mustConvert || err < 0)
+ m_frameData, s,
+ m_capImage->bits(), m_capDestFormat.fmt.pix.sizeimage);
+ if (ogl_use && m_mustConvert && err != -1)
+ displaybuf = m_capImage->bits();
+ if (ogl_use && !m_mustConvert)
+ displaybuf = m_frameData;
+ if (!ogl_use && !m_mustConvert)
memcpy(m_capImage->bits(), m_frameData, std::min(s, m_capImage->numBytes()));
break;
@@ -397,11 +450,14 @@ void ApplicationWindow::capFrame()
if (m_showFrames) {
if (m_mustConvert)
- err = v4lconvert_convert(m_convertData,
- &m_capSrcFormat, &m_capDestFormat,
- (unsigned char *)m_buffers[buf.index].start, buf.bytesused,
- m_capImage->bits(), m_capDestFormat.fmt.pix.sizeimage);
- if (!m_mustConvert || err < 0)
+ err = v4lconvert_convert(m_convertData, &m_capSrcFormat, &m_capDestFormat,
+ (unsigned char *)m_buffers[buf.index].start, buf.bytesused,
+ m_capImage->bits(), m_capDestFormat.fmt.pix.sizeimage);
+ if (ogl_use && m_mustConvert && err != -1)
+ displaybuf = m_capImage->bits();
+ if (ogl_use && !m_mustConvert)
+ displaybuf = (unsigned char *)m_buffers[buf.index].start;
+ if (!ogl_use && !m_mustConvert)
memcpy(m_capImage->bits(),
(unsigned char *)m_buffers[buf.index].start,
std::min(buf.bytesused, (unsigned)m_capImage->numBytes()));
@@ -425,11 +481,14 @@ void ApplicationWindow::capFrame()
if (m_showFrames) {
if (m_mustConvert)
- err = v4lconvert_convert(m_convertData,
- &m_capSrcFormat, &m_capDestFormat,
- (unsigned char *)buf.m.userptr, buf.bytesused,
- m_capImage->bits(), m_capDestFormat.fmt.pix.sizeimage);
- if (!m_mustConvert || err < 0)
+ err = v4lconvert_convert(m_convertData, &m_capSrcFormat, &m_capDestFormat,
+ (unsigned char *)buf.m.userptr, buf.bytesused,
+ m_capImage->bits(), m_capDestFormat.fmt.pix.sizeimage);
+ if (ogl_use && m_mustConvert && err != -1)
+ displaybuf = m_capImage->bits();
+ if (ogl_use && !m_mustConvert)
+ displaybuf = (unsigned char *)buf.m.userptr;
+ if (!ogl_use && !m_mustConvert)
memcpy(m_capImage->bits(), (unsigned char *)buf.m.userptr,
std::min(buf.bytesused, (unsigned)m_capImage->numBytes()));
}
@@ -458,8 +517,13 @@ void ApplicationWindow::capFrame()
m_tv = tv;
}
status = QString("Frame: %1 Fps: %2").arg(++m_frame).arg(m_fps);
- if (m_showFrames)
- m_capture->setImage(*m_capImage, status);
+ if (m_showFrames) {
+ if (ogl_use)
+ m_capture->setFrameOGL(m_capImage->width(), m_capImage->height(),
+ m_capSrcFormat.fmt.pix.pixelformat, displaybuf, status);
+ else
+ m_capture->setFrameCPU(*m_capImage, status);
+ }
curStatus = statusBar()->currentMessage();
if (curStatus.isEmpty() || curStatus.startsWith("Frame: "))
statusBar()->showMessage(status);
@@ -589,6 +653,7 @@ void ApplicationWindow::stopCapture()
v4l2_encoder_cmd cmd;
unsigned i;
+ m_capture->stop();
m_snapshotAct->setDisabled(true);
switch (m_capMethod) {
case methodRead:
@@ -638,7 +703,7 @@ void ApplicationWindow::closeCaptureWin()
m_capStartAct->setChecked(false);
}
-void ApplicationWindow::capStart(bool start)
+bool ApplicationWindow::isSupportedFormatCPU(v4l2_pix_format &format, QImage::Format &dstFmt)
{
static const struct {
__u32 v4l2_pixfmt;
@@ -658,8 +723,46 @@ void ApplicationWindow::capStart(bool start)
#endif
{ 0, QImage::Format_Invalid }
};
+
+ for (int i = 0; supported_fmts[i].v4l2_pixfmt; i++) {
+ if (supported_fmts[i].v4l2_pixfmt == format.pixelformat) {
+ dstFmt = supported_fmts[i].qt_pixfmt;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ApplicationWindow::isSupportedFormatOGL(v4l2_pix_format &format)
+{
+ __u32 supported_fmts[] = {
+ V4L2_PIX_FMT_RGB32,
+ V4L2_PIX_FMT_BGR32,
+ V4L2_PIX_FMT_RGB24,
+ V4L2_PIX_FMT_BGR24,
+ V4L2_PIX_FMT_RGB565,
+ V4L2_PIX_FMT_RGB555,
+ V4L2_PIX_FMT_YUYV,
+ V4L2_PIX_FMT_YVYU,
+ V4L2_PIX_FMT_UYVY,
+ V4L2_PIX_FMT_VYUY,
+ V4L2_PIX_FMT_YVU420,
+ V4L2_PIX_FMT_YUV420,
+ 0
+ };
+
+ for (int i = 0; supported_fmts[i]; i++)
+ if (supported_fmts[i] == format.pixelformat)
+ return true;
+
+ return false;
+}
+
+void ApplicationWindow::capStart(bool start)
+{
QImage::Format dstFmt = QImage::Format_RGB888;
struct v4l2_fract interval;
+ bool ogl_use = m_capture->isEnableOGL();
v4l2_pix_format &srcPix = m_capSrcFormat.fmt.pix;
v4l2_pix_format &dstPix = m_capDestFormat.fmt.pix;
@@ -722,7 +825,11 @@ void ApplicationWindow::capStart(bool start)
m_capture->setMinimumSize(m_vbiWidth, m_vbiHeight);
m_capImage = new QImage(m_vbiWidth, m_vbiHeight, dstFmt);
m_capImage->fill(0);
- m_capture->setImage(*m_capImage, "No frame");
+ if (ogl_use)
+ m_capture->setFrameOGL(m_capImage->width(), m_capImage->height(),
+ m_capSrcFormat.fmt.pix.pixelformat, m_capImage->bits(), "No frame");
+ else
+ m_capture->setFrameCPU(*m_capImage, "No frame");
m_capture->show();
}
statusBar()->showMessage("No frame");
@@ -744,14 +851,16 @@ void ApplicationWindow::capStart(bool start)
m_capDestFormat = m_capSrcFormat;
dstPix.pixelformat = V4L2_PIX_FMT_RGB24;
- for (int i = 0; supported_fmts[i].v4l2_pixfmt; i++) {
- if (supported_fmts[i].v4l2_pixfmt == srcPix.pixelformat) {
- dstPix.pixelformat = supported_fmts[i].v4l2_pixfmt;
- dstFmt = supported_fmts[i].qt_pixfmt;
- m_mustConvert = false;
- break;
- }
+ if (!ogl_use && isSupportedFormatCPU(srcPix, dstFmt)) {
+ dstPix.pixelformat = srcPix.pixelformat;
+ m_mustConvert = false;
+ }
+
+ if (ogl_use && isSupportedFormatOGL(srcPix)) {
+ dstPix.pixelformat = srcPix.pixelformat;
+ m_mustConvert = false;
}
+
if (m_mustConvert) {
v4l2_format copy = m_capSrcFormat;
@@ -765,7 +874,11 @@ void ApplicationWindow::capStart(bool start)
m_capture->setMinimumSize(dstPix.width, dstPix.height);
m_capImage = new QImage(dstPix.width, dstPix.height, dstFmt);
m_capImage->fill(0);
- m_capture->setImage(*m_capImage, "No frame");
+ if (ogl_use)
+ m_capture->setFrameOGL(m_capImage->width(), m_capImage->height(),
+ m_capSrcFormat.fmt.pix.pixelformat, m_capImage->bits(), "No frame");
+ else
+ m_capture->setFrameCPU(*m_capImage, "No frame");
m_capture->show();
}
@@ -890,7 +1003,7 @@ void ApplicationWindow::saveRaw(bool checked)
void ApplicationWindow::about()
{
QMessageBox::about(this, "V4L2 Test Bench",
- "This program allows easy experimenting with video4linux devices.");
+ "This program allows easy experimenting with video4linux devices.");
}
void ApplicationWindow::error(const QString &error)
diff --git a/utils/qv4l2/qv4l2.h b/utils/qv4l2/qv4l2.h
index 8634948..27cb72d 100644
--- a/utils/qv4l2/qv4l2.h
+++ b/utils/qv4l2/qv4l2.h
@@ -91,6 +91,9 @@ private:
void stopCapture();
void startOutput(unsigned buffer_size);
void stopOutput();
+ bool isSupportedFormatCPU(v4l2_pix_format &format, QImage::Format &dstFmt);
+ bool isSupportedFormatOGL(v4l2_pix_format &format);
+
struct buffer *m_buffers;
struct v4l2_format m_capSrcFormat;
struct v4l2_format m_capDestFormat;
@@ -108,6 +111,7 @@ private slots:
void snapshot();
void capVbiFrame();
void saveRaw(bool);
+ void setPreviewMode();
// gui
private slots:
@@ -186,6 +190,10 @@ private:
unsigned m_fps;
struct timeval m_tv;
QFile m_saveRaw;
+#ifdef ENABLE_OGL
+ QAction *m_previewAct;
+#endif
+
};
extern ApplicationWindow *g_mw;
--
1.8.3.2
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH 4/4] qv4l2: add OpenGL video render
2013-07-16 11:24 ` [PATCH 4/4] qv4l2: add OpenGL video render Bård Eirik Winther
@ 2013-07-16 12:01 ` Laurent Pinchart
2013-07-16 12:59 ` Bard Eirik Winther
0 siblings, 1 reply; 9+ messages in thread
From: Laurent Pinchart @ 2013-07-16 12:01 UTC (permalink / raw)
To: Bård Eirik Winther; +Cc: linux-media, hansverk
Hi Bård,
Thank you for the patches.
On Tuesday 16 July 2013 13:24:08 Bård Eirik Winther wrote:
> The qv4l2 test utility now supports OpenGL-accelerated display of video.
> This allows for using the graphics card to render the video content to
> screen and to performing color space conversion.
>
> Signed-off-by: Bård Eirik Winther <bwinther@cisco.com>
> ---
> configure.ac | 8 +-
> utils/qv4l2/Makefile.am | 9 +-
> utils/qv4l2/capture-win.cpp | 559 +++++++++++++++++++++++++++++++++++++++--
> utils/qv4l2/capture-win.h | 81 ++++++-
> utils/qv4l2/qv4l2.cpp | 173 +++++++++++---
> utils/qv4l2/qv4l2.h | 8 +
> 6 files changed, 782 insertions(+), 56 deletions(-)
Is there a chance you could split the OpenGL code to separate classes, in a
separate source file ? This would allow implementing other renderers, such as
KMS planes on embedded devices.
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 4/4] qv4l2: add OpenGL video render
2013-07-16 12:01 ` Laurent Pinchart
@ 2013-07-16 12:59 ` Bard Eirik Winther
2013-07-16 13:05 ` Laurent Pinchart
0 siblings, 1 reply; 9+ messages in thread
From: Bard Eirik Winther @ 2013-07-16 12:59 UTC (permalink / raw)
To: Laurent Pinchart; +Cc: linux-media, Hans Verkuil
On Tuesday, July 16, 2013 02:01:45 PM you wrote:
> Hi Bård,
>
> Thank you for the patches.
>
> On Tuesday 16 July 2013 13:24:08 Bård Eirik Winther wrote:
> > The qv4l2 test utility now supports OpenGL-accelerated display of video.
> > This allows for using the graphics card to render the video content to
> > screen and to performing color space conversion.
> >
> > Signed-off-by: Bård Eirik Winther <bwinther@cisco.com>
> > ---
> > configure.ac | 8 +-
> > utils/qv4l2/Makefile.am | 9 +-
> > utils/qv4l2/capture-win.cpp | 559 +++++++++++++++++++++++++++++++++++++++--
> > utils/qv4l2/capture-win.h | 81 ++++++-
> > utils/qv4l2/qv4l2.cpp | 173 +++++++++++---
> > utils/qv4l2/qv4l2.h | 8 +
> > 6 files changed, 782 insertions(+), 56 deletions(-)
>
> Is there a chance you could split the OpenGL code to separate classes, in a
> separate source file ? This would allow implementing other renderers, such as
> KMS planes on embedded devices.
>
Hi.
Do you mean to separate the GL class only or all the different shaders/renderes as well?
Bård Winther
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 4/4] qv4l2: add OpenGL video render
2013-07-16 12:59 ` Bard Eirik Winther
@ 2013-07-16 13:05 ` Laurent Pinchart
0 siblings, 0 replies; 9+ messages in thread
From: Laurent Pinchart @ 2013-07-16 13:05 UTC (permalink / raw)
To: Bard Eirik Winther; +Cc: linux-media, Hans Verkuil
Hi Bård,
On Tuesday 16 July 2013 14:59:04 Bard Eirik Winther wrote:
> On Tuesday, July 16, 2013 02:01:45 PM you wrote:
> > Hi Bård,
> >
> > Thank you for the patches.
> >
> > On Tuesday 16 July 2013 13:24:08 Bård Eirik Winther wrote:
> > > The qv4l2 test utility now supports OpenGL-accelerated display of video.
> > > This allows for using the graphics card to render the video content to
> > > screen and to performing color space conversion.
> > >
> > > Signed-off-by: Bård Eirik Winther <bwinther@cisco.com>
> > > ---
> > >
> > > configure.ac | 8 +-
> > > utils/qv4l2/Makefile.am | 9 +-
> > > utils/qv4l2/capture-win.cpp | 559 +++++++++++++++++++++++++++++++++++--
> > > utils/qv4l2/capture-win.h | 81 ++++++-
> > > utils/qv4l2/qv4l2.cpp | 173 +++++++++++---
> > > utils/qv4l2/qv4l2.h | 8 +
> > > 6 files changed, 782 insertions(+), 56 deletions(-)
> >
> > Is there a chance you could split the OpenGL code to separate classes, in
> > a
> > separate source file ? This would allow implementing other renderers, such
> > as KMS planes on embedded devices.
>
> Hi.
>
> Do you mean to separate the GL class only or all the different
> shaders/renderes as well?
Basically, what would be nice to get is an easy way to extend qv4l2 with
different renderers. OpenGL is fine on the desktop, but for embedded devices a
KMS planes backend would work best given the mess that the embedded GPU
situation is. Instead of adding #ifdef ENABLE_OGL and if (use_ogl) through the
code, abstracting the rendering code in a separate base class that renderers
could inherit from would make the code simpler to read, maintain and extend.
I haven't looked at the details so I'm not sure how much work that would be,
but if the effort is reasonable I think it would be a nice improvement.
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 4/4] qv4l2: add OpenGL video render
[not found] <201307171303.44303.hansverk@cisco.com>
@ 2013-07-17 12:09 ` Bård Eirik Winther
0 siblings, 0 replies; 9+ messages in thread
From: Bård Eirik Winther @ 2013-07-17 12:09 UTC (permalink / raw)
To: linux-media; +Cc: Hans Verkuil, laurent.pinchart
> On Wednesday, July 17, 2013 01:03:44 PM you wrote:
> Hi Bård,
>
> On Tuesday 16 July 2013 14:59:04 Bard Eirik Winther wrote:
> > On Tuesday, July 16, 2013 02:01:45 PM you wrote:
> > > Hi Bård,
> > >
> > > Thank you for the patches.
> > >
> > > On Tuesday 16 July 2013 13:24:08 Bård Eirik Winther wrote:
> > > > The qv4l2 test utility now supports OpenGL-accelerated display of video.
> > > > This allows for using the graphics card to render the video content to
> > > > screen and to performing color space conversion.
> > > >
> > > > Signed-off-by: Bård Eirik Winther <bwinther@cisco.com>
> > > > ---
> > > >
> > > > configure.ac | 8 +-
> > > > utils/qv4l2/Makefile.am | 9 +-
> > > > utils/qv4l2/capture-win.cpp | 559 +++++++++++++++++++++++++++++++++++--
> > > > utils/qv4l2/capture-win.h | 81 ++++++-
> > > > utils/qv4l2/qv4l2.cpp | 173 +++++++++++---
> > > > utils/qv4l2/qv4l2.h | 8 +
> > > > 6 files changed, 782 insertions(+), 56 deletions(-)
> > >
> > > Is there a chance you could split the OpenGL code to separate classes, in
> > > a
> > > separate source file ? This would allow implementing other renderers, such
> > > as KMS planes on embedded devices.
> >
> > Hi.
> >
> > Do you mean to separate the GL class only or all the different
> > shaders/renderes as well?
>
> Basically, what would be nice to get is an easy way to extend qv4l2 with
> different renderers. OpenGL is fine on the desktop, but for embedded devices a
> KMS planes backend would work best given the mess that the embedded GPU
> situation is. Instead of adding #ifdef ENABLE_OGL and if (use_ogl) through the
> code, abstracting the rendering code in a separate base class that renderers
> could inherit from would make the code simpler to read, maintain and extend.
>
> I haven't looked at the details so I'm not sure how much work that would be,
> but if the effort is reasonable I think it would be a nice improvement.
>
>
I belive I have found a workable solution that should not take that much of time to implement.
The current interface for adding more render/display options is simply to extend this class:
class CaptureCanvas {
public:
CaptureCanvas(){}
virtual ~CaptureCanvas();
virtual void setFrame(int width, int height, unsigned char *data, __u32 format);
virtual void start();
virtual void stop();
virtual void hasNativeFormat(__u32 format);
virtual static bool isSupportedRender();
};
It should cover any needs for display afaik, but then again I do not know every system that exists which might require more interaction.
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2013-07-17 12:09 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-07-16 11:24 [PATCH 0/4] OpenGL render for qv4l2 Bård Eirik Winther
2013-07-16 11:24 ` [PATCH 1/4] qv4l2: move function ctrlEvent Bård Eirik Winther
2013-07-16 11:24 ` [PATCH 2/4] qv4l2: add hotkeys for common operations Bård Eirik Winther
2013-07-16 11:24 ` [PATCH 3/4] qv4l2: fix CaptureWin mimimum size to match video fram size Bård Eirik Winther
2013-07-16 11:24 ` [PATCH 4/4] qv4l2: add OpenGL video render Bård Eirik Winther
2013-07-16 12:01 ` Laurent Pinchart
2013-07-16 12:59 ` Bard Eirik Winther
2013-07-16 13:05 ` Laurent Pinchart
[not found] <201307171303.44303.hansverk@cisco.com>
2013-07-17 12:09 ` Bård Eirik Winther
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox