* [PATCHv2 FINAL 5/6] qv4l2: new modular capture window design
2013-07-30 8:15 ` [PATCHv2 FINAL 1/6] qv4l2: move function ctrlEvent Bård Eirik Winther
` (2 preceding siblings ...)
2013-07-30 8:15 ` [PATCHv2 FINAL 4/6] qv4l2: add Capture menu Bård Eirik Winther
@ 2013-07-30 8:15 ` Bård Eirik Winther
2013-07-30 8:15 ` [PATCHv2 FINAL 6/6] qv4l2: add OpenGL rendering Bård Eirik Winther
4 siblings, 0 replies; 12+ messages in thread
From: Bård Eirik Winther @ 2013-07-30 8:15 UTC (permalink / raw)
To: linux-media
The display of video has been divided into classes to
easier implement other ways to render frames on screen.
Signed-off-by: Bård Eirik Winther <bwinther@cisco.com>
---
utils/qv4l2/Makefile.am | 4 +-
utils/qv4l2/capture-win-qt.cpp | 89 +++++++++++++++++++++++++++++++++++++++
utils/qv4l2/capture-win-qt.h | 47 +++++++++++++++++++++
utils/qv4l2/capture-win.cpp | 45 ++++++++++----------
utils/qv4l2/capture-win.h | 24 ++++++-----
utils/qv4l2/qv4l2.cpp | 94 +++++++++++++++++++-----------------------
utils/qv4l2/qv4l2.h | 1 +
7 files changed, 219 insertions(+), 85 deletions(-)
create mode 100644 utils/qv4l2/capture-win-qt.cpp
create mode 100644 utils/qv4l2/capture-win-qt.h
diff --git a/utils/qv4l2/Makefile.am b/utils/qv4l2/Makefile.am
index 1f5a49f..9ef8149 100644
--- a/utils/qv4l2/Makefile.am
+++ b/utils/qv4l2/Makefile.am
@@ -1,6 +1,7 @@
bin_PROGRAMS = qv4l2
qv4l2_SOURCES = qv4l2.cpp general-tab.cpp ctrl-tab.cpp vbi-tab.cpp v4l2-api.cpp capture-win.cpp \
+ capture-win-qt.cpp capture-win-qt.h \
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
@@ -37,5 +38,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-qt.cpp b/utils/qv4l2/capture-win-qt.cpp
new file mode 100644
index 0000000..63c77d5
--- /dev/null
+++ b/utils/qv4l2/capture-win-qt.cpp
@@ -0,0 +1,89 @@
+/* qv4l2: a control panel controlling v4l2 devices.
+ *
+ * Copyright (C) 2006 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "capture-win-qt.h"
+
+CaptureWinQt::CaptureWinQt() :
+ m_frame(new QImage(0, 0, QImage::Format_Invalid))
+{
+
+ CaptureWin::buildWindow(&m_videoSurface);
+}
+
+CaptureWinQt::~CaptureWinQt()
+{
+ delete m_frame;
+}
+
+void CaptureWinQt::setFrame(int width, int height, __u32 format, unsigned char *data, const QString &info)
+{
+ QImage::Format dstFmt;
+ bool supported = findNativeFormat(format, dstFmt);
+ if (!supported)
+ dstFmt = QImage::Format_RGB888;
+
+ if (m_frame->width() != width || m_frame->height() != height || m_frame->format() != dstFmt) {
+ delete m_frame;
+ m_frame = new QImage(width, height, dstFmt);
+ }
+
+ if (data == NULL || !supported)
+ m_frame->fill(0);
+ else
+ memcpy(m_frame->bits(), data, m_frame->numBytes());
+
+ m_information.setText(info);
+ m_videoSurface.setPixmap(QPixmap::fromImage(*m_frame));
+}
+
+bool CaptureWinQt::hasNativeFormat(__u32 format)
+{
+ QImage::Format fmt;
+ return findNativeFormat(format, fmt);
+}
+
+bool CaptureWinQt::findNativeFormat(__u32 format, QImage::Format &dstFmt)
+{
+ static const struct {
+ __u32 v4l2_pixfmt;
+ QImage::Format qt_pixfmt;
+ } supported_fmts[] = {
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ { V4L2_PIX_FMT_RGB32, QImage::Format_RGB32 },
+ { V4L2_PIX_FMT_RGB24, QImage::Format_RGB888 },
+ { V4L2_PIX_FMT_RGB565X, QImage::Format_RGB16 },
+ { V4L2_PIX_FMT_RGB555X, QImage::Format_RGB555 },
+#else
+ { V4L2_PIX_FMT_BGR32, QImage::Format_RGB32 },
+ { V4L2_PIX_FMT_RGB24, QImage::Format_RGB888 },
+ { V4L2_PIX_FMT_RGB565, QImage::Format_RGB16 },
+ { V4L2_PIX_FMT_RGB555, QImage::Format_RGB555 },
+ { V4L2_PIX_FMT_RGB444, QImage::Format_RGB444 },
+#endif
+ { 0, QImage::Format_Invalid }
+ };
+
+ for (int i = 0; supported_fmts[i].v4l2_pixfmt; i++) {
+ if (supported_fmts[i].v4l2_pixfmt == format) {
+ dstFmt = supported_fmts[i].qt_pixfmt;
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/utils/qv4l2/capture-win-qt.h b/utils/qv4l2/capture-win-qt.h
new file mode 100644
index 0000000..d3b4fe8
--- /dev/null
+++ b/utils/qv4l2/capture-win-qt.h
@@ -0,0 +1,47 @@
+/* qv4l2: a control panel controlling v4l2 devices.
+ *
+ * Copyright (C) 2006 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef CAPTURE_WIN_QT_H
+#define CAPTURE_WIN_QT_H
+
+#include "qv4l2.h"
+#include "capture-win.h"
+
+#include <QLabel>
+#include <QImage>
+
+class CaptureWinQt : public CaptureWin
+{
+public:
+ CaptureWinQt();
+ ~CaptureWinQt();
+
+ void setFrame(int width, int height, __u32 format,
+ unsigned char *data, const QString &info);
+
+ bool hasNativeFormat(__u32 format);
+ static bool isSupported() { return true; }
+
+private:
+ bool findNativeFormat(__u32 format, QImage::Format &dstFmt);
+
+ QImage *m_frame;
+ QLabel m_videoSurface;
+};
+#endif
diff --git a/utils/qv4l2/capture-win.cpp b/utils/qv4l2/capture-win.cpp
index 68dc9ed..2d57909 100644
--- a/utils/qv4l2/capture-win.cpp
+++ b/utils/qv4l2/capture-win.cpp
@@ -16,35 +16,42 @@
* 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 "capture-win.h"
+
+#include <QCloseEvent>
#include <QLabel>
#include <QImage>
#include <QVBoxLayout>
-#include <QCloseEvent>
#include <QApplication>
#include <QDesktopWidget>
-#include "qv4l2.h"
-#include "capture-win.h"
-
CaptureWin::CaptureWin()
{
- QVBoxLayout *vbox = new QVBoxLayout(this);
-
setWindowTitle("V4L2 Capture");
- m_label = new QLabel();
- m_msg = new QLabel("No frame");
+ m_hotkeyClose = new QShortcut(Qt::CTRL+Qt::Key_W, this);
+ QObject::connect(m_hotkeyClose, SIGNAL(activated()), this, SLOT(close()));
+}
- vbox->addWidget(m_label);
- vbox->addWidget(m_msg);
+CaptureWin::~CaptureWin()
+{
+ if (layout() == NULL)
+ return;
- hotkeyClose = new QShortcut(Qt::CTRL+Qt::Key_W, this);
- QObject::connect(hotkeyClose, SIGNAL(activated()), this, SLOT(close()));
+ layout()->removeWidget(this);
+ delete layout();
+ delete m_hotkeyClose;
}
-CaptureWin::~CaptureWin()
+void CaptureWin::buildWindow(QWidget *videoSurface)
{
- delete hotkeyClose;
+ int l, t, r, b;
+ QVBoxLayout *vbox = new QVBoxLayout(this);
+ m_information.setText("No Frame");
+ vbox->addWidget(videoSurface, 2000);
+ vbox->addWidget(&m_information, 1, Qt::AlignBottom);
+ vbox->getContentsMargins(&l, &t, &r, &b);
+ vbox->setSpacing(b);
}
void CaptureWin::setMinimumSize(int minw, int minh)
@@ -56,7 +63,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();
@@ -74,12 +81,6 @@ void CaptureWin::setMinimumSize(int minw, int minh)
QWidget::setMaximumSize(maxSize.width(), maxSize.height());
}
-void CaptureWin::setImage(const QImage &image, const QString &status)
-{
- m_label->setPixmap(QPixmap::fromImage(image));
- m_msg->setText(status);
-}
-
void CaptureWin::closeEvent(QCloseEvent *event)
{
QWidget::closeEvent(event);
diff --git a/utils/qv4l2/capture-win.h b/utils/qv4l2/capture-win.h
index 3925757..c3b7d98 100644
--- a/utils/qv4l2/capture-win.h
+++ b/utils/qv4l2/capture-win.h
@@ -20,12 +20,11 @@
#ifndef CAPTURE_WIN_H
#define CAPTURE_WIN_H
+#include "qv4l2.h"
+
#include <QWidget>
#include <QShortcut>
-#include <sys/time.h>
-
-class QImage;
-class QLabel;
+#include <QLabel>
class CaptureWin : public QWidget
{
@@ -36,18 +35,23 @@ public:
~CaptureWin();
void setMinimumSize(int minw, int minh);
- void setImage(const QImage &image, const QString &status);
+ virtual void setFrame(int width, int height, __u32 format,
+ unsigned char *data, const QString &info) = 0;
+
+ virtual bool hasNativeFormat(__u32 format) = 0;
+ static bool isSupported() { return false; }
protected:
- virtual void closeEvent(QCloseEvent *event);
+ void closeEvent(QCloseEvent *event);
+ void buildWindow(QWidget *videoSurface);
+
+ QLabel m_information;
signals:
void close();
private:
- QLabel *m_label;
- QLabel *m_msg;
- QShortcut *hotkeyClose;
-};
+ QShortcut *m_hotkeyClose;
+};
#endif
diff --git a/utils/qv4l2/qv4l2.cpp b/utils/qv4l2/qv4l2.cpp
index 80937db..0c9b74c 100644
--- a/utils/qv4l2/qv4l2.cpp
+++ b/utils/qv4l2/qv4l2.cpp
@@ -21,6 +21,7 @@
#include "general-tab.h"
#include "vbi-tab.h"
#include "capture-win.h"
+#include "capture-win-qt.h"
#include <QToolBar>
#include <QToolButton>
@@ -160,7 +161,7 @@ void ApplicationWindow::setDevice(const QString &device, bool rawOpen)
if (!open(device, !rawOpen))
return;
- m_capture = new CaptureWin;
+ m_capture = new CaptureWinQt;
m_capture->setMinimumSize(150, 50);
connect(m_capture, SIGNAL(close()), this, SLOT(closeCaptureWin()));
@@ -347,7 +348,9 @@ void ApplicationWindow::capVbiFrame()
}
status = QString("Frame: %1 Fps: %2").arg(++m_frame).arg(m_fps);
if (m_showFrames)
- m_capture->setImage(*m_capImage, status);
+ m_capture->setFrame(m_capImage->width(), m_capImage->height(),
+ m_capDestFormat.fmt.pix.pixelformat, m_capImage->bits(), status);
+
curStatus = statusBar()->currentMessage();
if (curStatus.isEmpty() || curStatus.startsWith("Frame: "))
statusBar()->showMessage(status);
@@ -363,6 +366,8 @@ void ApplicationWindow::capFrame()
int err = 0;
bool again;
+ unsigned char *displaybuf = NULL;
+
switch (m_capMethod) {
case methodRead:
s = read(m_frameData, m_capSrcFormat.fmt.pix.sizeimage);
@@ -382,10 +387,12 @@ 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)
- memcpy(m_capImage->bits(), m_frameData, std::min(s, m_capImage->numBytes()));
+ m_frameData, s,
+ m_capImage->bits(), m_capDestFormat.fmt.pix.sizeimage);
+ if (m_mustConvert && err != -1)
+ displaybuf = m_capImage->bits();
+ if (!m_mustConvert)
+ displaybuf = m_frameData;
break;
case methodMmap:
@@ -399,21 +406,19 @@ 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)
- memcpy(m_capImage->bits(),
- (unsigned char *)m_buffers[buf.index].start,
- std::min(buf.bytesused, (unsigned)m_capImage->numBytes()));
+ 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 != -1)
+ displaybuf = m_capImage->bits();
+ if (!m_mustConvert)
+ displaybuf = (unsigned char *)m_buffers[buf.index].start;
}
if (m_makeSnapshot)
makeSnapshot((unsigned char *)m_buffers[buf.index].start, buf.bytesused);
if (m_saveRaw.openMode())
m_saveRaw.write((const char *)m_buffers[buf.index].start, buf.bytesused);
- qbuf(buf);
break;
case methodUser:
@@ -427,20 +432,19 @@ 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)
- memcpy(m_capImage->bits(), (unsigned char *)buf.m.userptr,
- std::min(buf.bytesused, (unsigned)m_capImage->numBytes()));
+ 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 != -1)
+ displaybuf = m_capImage->bits();
+ if (!m_mustConvert)
+ displaybuf = (unsigned char *)buf.m.userptr;
}
if (m_makeSnapshot)
makeSnapshot((unsigned char *)buf.m.userptr, buf.bytesused);
if (m_saveRaw.openMode())
m_saveRaw.write((const char *)buf.m.userptr, buf.bytesused);
- qbuf(buf);
break;
}
if (err == -1 && m_frame == 0)
@@ -460,8 +464,15 @@ void ApplicationWindow::capFrame()
m_tv = tv;
}
status = QString("Frame: %1 Fps: %2").arg(++m_frame).arg(m_fps);
+ if (displaybuf == NULL && m_showFrames)
+ status.append(" Error: Unsupported format.");
if (m_showFrames)
- m_capture->setImage(*m_capImage, status);
+ m_capture->setFrame(m_capImage->width(), m_capImage->height(),
+ m_capDestFormat.fmt.pix.pixelformat, displaybuf, status);
+
+ if (m_capMethod == methodMmap || m_capMethod == methodUser)
+ qbuf(buf);
+
curStatus = statusBar()->currentMessage();
if (curStatus.isEmpty() || curStatus.startsWith("Frame: "))
statusBar()->showMessage(status);
@@ -642,24 +653,6 @@ void ApplicationWindow::closeCaptureWin()
void ApplicationWindow::capStart(bool start)
{
- static const struct {
- __u32 v4l2_pixfmt;
- QImage::Format qt_pixfmt;
- } supported_fmts[] = {
-#if Q_BYTE_ORDER == Q_BIG_ENDIAN
- { V4L2_PIX_FMT_RGB32, QImage::Format_RGB32 },
- { V4L2_PIX_FMT_RGB24, QImage::Format_RGB888 },
- { V4L2_PIX_FMT_RGB565X, QImage::Format_RGB16 },
- { V4L2_PIX_FMT_RGB555X, QImage::Format_RGB555 },
-#else
- { V4L2_PIX_FMT_BGR32, QImage::Format_RGB32 },
- { V4L2_PIX_FMT_RGB24, QImage::Format_RGB888 },
- { V4L2_PIX_FMT_RGB565, QImage::Format_RGB16 },
- { V4L2_PIX_FMT_RGB555, QImage::Format_RGB555 },
- { V4L2_PIX_FMT_RGB444, QImage::Format_RGB444 },
-#endif
- { 0, QImage::Format_Invalid }
- };
QImage::Format dstFmt = QImage::Format_RGB888;
struct v4l2_fract interval;
v4l2_pix_format &srcPix = m_capSrcFormat.fmt.pix;
@@ -724,7 +717,8 @@ 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");
+ m_capture->setFrame(m_capImage->width(), m_capImage->height(),
+ m_capDestFormat.fmt.pix.pixelformat, m_capImage->bits(), "No frame");
m_capture->show();
}
statusBar()->showMessage("No frame");
@@ -746,14 +740,11 @@ 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 (m_capture->hasNativeFormat(srcPix.pixelformat)) {
+ dstPix.pixelformat = srcPix.pixelformat;
+ m_mustConvert = false;
}
+
if (m_mustConvert) {
v4l2_format copy = m_capSrcFormat;
@@ -767,7 +758,8 @@ 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");
+ m_capture->setFrame(m_capImage->width(), m_capImage->height(),
+ m_capDestFormat.fmt.pix.pixelformat, m_capImage->bits(), "No frame");
m_capture->show();
}
diff --git a/utils/qv4l2/qv4l2.h b/utils/qv4l2/qv4l2.h
index 8634948..ccfc2f9 100644
--- a/utils/qv4l2/qv4l2.h
+++ b/utils/qv4l2/qv4l2.h
@@ -33,6 +33,7 @@
#include "v4l2-api.h"
#include "raw2sliced.h"
+#include "capture-win.h"
class QComboBox;
class QSpinBox;
--
1.8.3.2
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCHv2 FINAL 6/6] qv4l2: add OpenGL rendering
2013-07-30 8:15 ` [PATCHv2 FINAL 1/6] qv4l2: move function ctrlEvent Bård Eirik Winther
` (3 preceding siblings ...)
2013-07-30 8:15 ` [PATCHv2 FINAL 5/6] qv4l2: new modular capture window design Bård Eirik Winther
@ 2013-07-30 8:15 ` Bård Eirik Winther
2013-08-01 11:05 ` Bård Eirik Winther
4 siblings, 1 reply; 12+ messages in thread
From: Bård Eirik Winther @ 2013-07-30 8:15 UTC (permalink / raw)
To: linux-media
Adds OpenGL-accelerated display of video.
This allows for using the graphics card to render
the video content to screen and to perform color space conversion.
Signed-off-by: Bård Eirik Winther <bwinther@cisco.com>
---
configure.ac | 8 +-
utils/qv4l2/Makefile.am | 8 +-
utils/qv4l2/capture-win-gl.cpp | 553 +++++++++++++++++++++++++++++++++++++++++
utils/qv4l2/capture-win-gl.h | 96 +++++++
utils/qv4l2/capture-win-qt.h | 1 +
utils/qv4l2/capture-win.h | 39 +++
utils/qv4l2/qv4l2.cpp | 54 +++-
utils/qv4l2/qv4l2.h | 10 +
8 files changed, 765 insertions(+), 4 deletions(-)
create mode 100644 utils/qv4l2/capture-win-gl.cpp
create mode 100644 utils/qv4l2/capture-win-gl.h
diff --git a/configure.ac b/configure.ac
index e249546..d74da61 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(QTGL, [QtOpenGL >= 4.4 gl], [qt_pkgconfig_gl=true], [qt_pkgconfig_gl=false])
+if test "x$qt_pkgconfig_gl" = "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_GL], [test WITH_QV4L2 -a ${qt_pkgconfig_gl} = 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 9ef8149..22d4c17 100644
--- a/utils/qv4l2/Makefile.am
+++ b/utils/qv4l2/Makefile.am
@@ -1,12 +1,18 @@
bin_PROGRAMS = qv4l2
qv4l2_SOURCES = qv4l2.cpp general-tab.cpp ctrl-tab.cpp vbi-tab.cpp v4l2-api.cpp capture-win.cpp \
- capture-win-qt.cpp capture-win-qt.h \
+ capture-win-qt.cpp capture-win-qt.h capture-win-gl.cpp capture-win-gl.h \
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_GL
+qv4l2_CPPFLAGS = $(QTGL_CFLAGS) -DENABLE_GL
+qv4l2_LDFLAGS = $(QTGL_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 \
diff --git a/utils/qv4l2/capture-win-gl.cpp b/utils/qv4l2/capture-win-gl.cpp
new file mode 100644
index 0000000..807d9e9
--- /dev/null
+++ b/utils/qv4l2/capture-win-gl.cpp
@@ -0,0 +1,553 @@
+/*
+ * The YUY2 shader code was copied and simplified from face-responder. The code is under public domain:
+ * https://bitbucket.org/nateharward/face-responder/src/0c3b4b957039d9f4bf1da09b9471371942de2601/yuv42201_laplace.frag?at=master
+ *
+ * All other OpenGL code:
+ *
+ * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "capture-win-gl.h"
+
+#include <stdio.h>
+
+CaptureWinGL::CaptureWinGL()
+{
+ CaptureWin::buildWindow(&m_videoSurface);
+ CaptureWin::setWindowTitle("V4L2 Capture (OpenGL)");
+}
+
+CaptureWinGL::~CaptureWinGL()
+{
+}
+
+void CaptureWinGL::stop()
+{
+#ifdef ENABLE_GL
+ m_videoSurface.stop();
+#endif
+}
+
+void CaptureWinGL::setFrame(int width, int height, __u32 format, unsigned char *data, const QString &info)
+{
+#ifdef ENABLE_GL
+ m_videoSurface.setFrame(width, height, format, data);
+#endif
+ m_information.setText(info);
+}
+
+bool CaptureWinGL::hasNativeFormat(__u32 format)
+{
+#ifdef ENABLE_GL
+ return m_videoSurface.hasNativeFormat(format);
+#else
+ return false;
+#endif
+}
+
+bool CaptureWinGL::isSupported()
+{
+#ifdef ENABLE_GL
+ return true;
+#else
+ return false;
+#endif
+}
+
+#ifdef ENABLE_GL
+CaptureWinGLEngine::CaptureWinGLEngine() :
+ m_frameHeight(0),
+ m_frameWidth(0),
+ m_screenTextureCount(0),
+ m_formatChange(false),
+ m_frameFormat(0),
+ m_frameData(NULL)
+{
+ m_glfunction.initializeGLFunctions(context());
+}
+
+CaptureWinGLEngine::~CaptureWinGLEngine()
+{
+ clearShader();
+}
+
+void CaptureWinGLEngine::clearShader()
+{
+ glDeleteTextures(m_screenTextureCount, m_screenTexture);
+ m_shaderProgram.release();
+ m_shaderProgram.removeAllShaders();
+}
+
+void CaptureWinGLEngine::stop()
+{
+ // Setting the m_frameData to NULL stops OpenGL
+ // from updating frames on repaint
+ m_frameData = NULL;
+}
+
+void CaptureWinGLEngine::initializeGL()
+{
+ glShadeModel(GL_FLAT);
+ glEnable(GL_TEXTURE_2D);
+
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ checkError("InitializeGL");
+}
+
+
+void CaptureWinGLEngine::resizeGL(int width, int height)
+{
+ // Resizing is disabled by setting viewport equal to frame size
+ glViewport(0, 0, m_frameWidth, m_frameHeight);
+}
+
+void CaptureWinGLEngine::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;
+
+ QGLWidget::setMaximumSize(m_frameWidth, m_frameHeight);
+ }
+
+ m_frameData = data;
+ updateGL();
+}
+
+void CaptureWinGLEngine::checkError(const char *msg)
+{
+ int err = glGetError();
+ if (err) fprintf(stderr, "OpenGL Error 0x%x: %s.\n", err, msg);
+}
+
+bool CaptureWinGLEngine::hasNativeFormat(__u32 format)
+{
+ static const __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)
+ return true;
+
+ return false;
+}
+
+void CaptureWinGLEngine::changeShader()
+{
+ m_formatChange = false;
+ clearShader();
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, m_frameWidth, m_frameHeight, 0, 0, 1);
+ resizeGL(QGLWidget::width(), QGLWidget::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;
+ }
+
+ glClear(GL_COLOR_BUFFER_BIT);
+}
+
+void CaptureWinGLEngine::paintGL()
+{
+ if (m_frameWidth < 1 || m_frameHeight < 1) {
+ return;
+ }
+
+ if (m_formatChange)
+ changeShader();
+
+ if (m_frameData == NULL) {
+ return;
+ }
+
+ 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 CaptureWinGLEngine::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 CaptureWinGLEngine::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 CaptureWinGLEngine::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 CaptureWinGLEngine::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 CaptureWinGLEngine::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 CaptureWinGLEngine::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 CaptureWinGLEngine::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 CaptureWinGLEngine::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-gl.h b/utils/qv4l2/capture-win-gl.h
new file mode 100644
index 0000000..08e72b2
--- /dev/null
+++ b/utils/qv4l2/capture-win-gl.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef CAPTURE_WIN_GL_H
+#define CAPTURE_WIN_GL_H
+
+#include "qv4l2.h"
+#include "capture-win.h"
+
+#ifdef ENABLE_GL
+#define GL_GLEXT_PROTOTYPES
+#include <QGLWidget>
+#include <QGLShader>
+#include <QGLShaderProgram>
+#include <QGLFunctions>
+
+// This must be equal to the max number of textures that any shader uses
+#define MAX_TEXTURES_NEEDED 3
+
+class CaptureWinGLEngine : public QGLWidget
+{
+public:
+ CaptureWinGLEngine();
+ ~CaptureWinGLEngine();
+
+ void stop();
+ void setFrame(int width, int height, __u32 format, unsigned char *data);
+ bool hasNativeFormat(__u32 format);
+
+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[MAX_TEXTURES_NEEDED];
+ QGLFunctions m_glfunction;
+ unsigned char *m_frameData;
+ QGLShaderProgram m_shaderProgram;
+};
+
+#endif
+
+class CaptureWinGL : public CaptureWin
+{
+public:
+ CaptureWinGL();
+ ~CaptureWinGL();
+
+ void setFrame(int width, int height, __u32 format,
+ unsigned char *data, const QString &info);
+ void stop();
+ bool hasNativeFormat(__u32 format);
+ static bool isSupported();
+
+#ifdef ENABLE_GL
+ CaptureWinGLEngine m_videoSurface;
+#endif
+};
+
+#endif
diff --git a/utils/qv4l2/capture-win-qt.h b/utils/qv4l2/capture-win-qt.h
index d3b4fe8..d192045 100644
--- a/utils/qv4l2/capture-win-qt.h
+++ b/utils/qv4l2/capture-win-qt.h
@@ -35,6 +35,7 @@ public:
void setFrame(int width, int height, __u32 format,
unsigned char *data, const QString &info);
+ void stop(){}
bool hasNativeFormat(__u32 format);
static bool isSupported() { return true; }
diff --git a/utils/qv4l2/capture-win.h b/utils/qv4l2/capture-win.h
index c3b7d98..ca60244 100644
--- a/utils/qv4l2/capture-win.h
+++ b/utils/qv4l2/capture-win.h
@@ -35,16 +35,55 @@ public:
~CaptureWin();
void setMinimumSize(int minw, int minh);
+
+ /**
+ * @brief Set a frame into the capture window.
+ *
+ * When called the capture stream is
+ * either already running or starting for the first time.
+ *
+ * @param width Frame width in pixels
+ * @param height Frame height in pixels
+ * @param format The frame's format, given as a V4L2_PIX_FMT.
+ * @param data The frame data.
+ * @param info A string containing capture information.
+ */
virtual void setFrame(int width, int height, __u32 format,
unsigned char *data, const QString &info) = 0;
+ /**
+ * @brief Called when the capture stream is stopped.
+ */
+ virtual void stop() = 0;
+
+ /**
+ * @brief Queries the current capture window for its supported formats.
+ *
+ * Unsupported formats are converted by v4lconvert_convert().
+ *
+ * @param format The frame format question, given as a V4L2_PIX_FMT.
+ * @return true if the format is supported, false if not.
+ */
virtual bool hasNativeFormat(__u32 format) = 0;
+
+ /**
+ * @brief Defines wether a capture window is supported.
+ *
+ * By default nothing is supported, but derived classes can override this.
+ *
+ * @return true if the capture window is supported on the system, false if not.
+ */
static bool isSupported() { return false; }
protected:
void closeEvent(QCloseEvent *event);
void buildWindow(QWidget *videoSurface);
+ /**
+ * @brief A label that can is used to display capture information.
+ *
+ * @note This must be set in the derived class' setFrame() function.
+ */
QLabel m_information;
signals:
diff --git a/utils/qv4l2/qv4l2.cpp b/utils/qv4l2/qv4l2.cpp
index 0c9b74c..4dc5a3e 100644
--- a/utils/qv4l2/qv4l2.cpp
+++ b/utils/qv4l2/qv4l2.cpp
@@ -22,6 +22,7 @@
#include "vbi-tab.h"
#include "capture-win.h"
#include "capture-win-qt.h"
+#include "capture-win-gl.h"
#include <QToolBar>
#include <QToolButton>
@@ -130,6 +131,20 @@ ApplicationWindow::ApplicationWindow() :
QMenu *captureMenu = menuBar()->addMenu("&Capture");
captureMenu->addAction(m_capStartAct);
captureMenu->addAction(m_showFramesAct);
+ captureMenu->addSeparator();
+
+ if (CaptureWinGL::isSupported()) {
+ m_renderMethod = QV4L2_RENDER_GL;
+
+ m_useGLAct = new QAction("Use Open&GL Render", this);
+ m_useGLAct->setStatusTip("Use GPU with OpenGL for video capture if set.");
+ m_useGLAct->setCheckable(true);
+ m_useGLAct->setChecked(true);
+ connect(m_useGLAct, SIGNAL(triggered()), this, SLOT(setRenderMethod()));
+ captureMenu->addAction(m_useGLAct);
+ } else {
+ m_renderMethod = QV4L2_RENDER_QT;
+ }
QMenu *helpMenu = menuBar()->addMenu("&Help");
helpMenu->addAction("&About", this, SLOT(about()), Qt::Key_F1);
@@ -161,9 +176,9 @@ void ApplicationWindow::setDevice(const QString &device, bool rawOpen)
if (!open(device, !rawOpen))
return;
- m_capture = new CaptureWinQt;
+ newCaptureWin();
+
m_capture->setMinimumSize(150, 50);
- connect(m_capture, SIGNAL(close()), this, SLOT(closeCaptureWin()));
QWidget *w = new QWidget(m_tabs);
m_genTab = new GeneralTab(device, *this, 4, w);
@@ -206,6 +221,21 @@ void ApplicationWindow::openrawdev()
setDevice(d.selectedFiles().first(), true);
}
+void ApplicationWindow::setRenderMethod()
+{
+ if (m_capStartAct->isChecked()) {
+ m_useGLAct->setChecked(m_renderMethod == QV4L2_RENDER_GL);
+ return;
+ }
+
+ if (m_useGLAct->isChecked())
+ m_renderMethod = QV4L2_RENDER_GL;
+ else
+ m_renderMethod = QV4L2_RENDER_QT;
+
+ newCaptureWin();
+}
+
void ApplicationWindow::ctrlEvent()
{
v4l2_event ev;
@@ -253,6 +283,25 @@ void ApplicationWindow::ctrlEvent()
}
}
+void ApplicationWindow::newCaptureWin()
+{
+ if (m_capture != NULL) {
+ m_capture->stop();
+ delete m_capture;
+ }
+
+ switch (m_renderMethod) {
+ case QV4L2_RENDER_GL:
+ m_capture = new CaptureWinGL;
+ break;
+ default:
+ m_capture = new CaptureWinQt;
+ break;
+ }
+
+ connect(m_capture, SIGNAL(close()), this, SLOT(closeCaptureWin()));
+}
+
void ApplicationWindow::capVbiFrame()
{
__u32 buftype = m_genTab->bufType();
@@ -602,6 +651,7 @@ void ApplicationWindow::stopCapture()
v4l2_encoder_cmd cmd;
unsigned i;
+ m_capture->stop();
m_snapshotAct->setDisabled(true);
switch (m_capMethod) {
case methodRead:
diff --git a/utils/qv4l2/qv4l2.h b/utils/qv4l2/qv4l2.h
index ccfc2f9..2921b16 100644
--- a/utils/qv4l2/qv4l2.h
+++ b/utils/qv4l2/qv4l2.h
@@ -60,6 +60,11 @@ enum CapMethod {
methodUser
};
+enum RenderMethod {
+ QV4L2_RENDER_GL,
+ QV4L2_RENDER_QT
+};
+
struct buffer {
void *start;
size_t length;
@@ -92,6 +97,8 @@ private:
void stopCapture();
void startOutput(unsigned buffer_size);
void stopOutput();
+ void newCaptureWin();
+
struct buffer *m_buffers;
struct v4l2_format m_capSrcFormat;
struct v4l2_format m_capDestFormat;
@@ -101,6 +108,7 @@ private:
bool m_mustConvert;
CapMethod m_capMethod;
bool m_makeSnapshot;
+ RenderMethod m_renderMethod;
private slots:
void capStart(bool);
@@ -109,6 +117,7 @@ private slots:
void snapshot();
void capVbiFrame();
void saveRaw(bool);
+ void setRenderMethod();
// gui
private slots:
@@ -166,6 +175,7 @@ private:
QAction *m_snapshotAct;
QAction *m_saveRawAct;
QAction *m_showFramesAct;
+ QAction *m_useGLAct;
QString m_filename;
QSignalMapper *m_sigMapper;
QTabWidget *m_tabs;
--
1.8.3.2
^ permalink raw reply related [flat|nested] 12+ messages in thread