Some cleanup in the GStreamer plugin.

Removed QGstreamerVideoOverlay and related classes. It was used as
'Window' control but performs worse than QGstreamerVideoWindow which
does basically the same thing using GStreamer ready-made components
instead.
Removed X11 dependencies and related configuration tests. It was only
needed for QGstreamerVideoOverlay.

Change-Id: I2ad2636ccf0060e56cd64f3d9e5b3c24dc75f5a3
Reviewed-by: Andy Nichols <andy.nichols@digia.com>
This commit is contained in:
Yoann Lopes
2013-09-20 14:02:24 +02:00
committed by The Qt Project
parent ac029c65f2
commit c3ca3a760e
22 changed files with 35 additions and 1620 deletions

View File

@@ -49,6 +49,7 @@ PRIVATE_HEADERS += \
qgstcodecsinfo_p.h \
qgstreamervideoprobecontrol_p.h \
qgstreameraudioprobecontrol_p.h \
qgstreamervideowindow_p.h
SOURCES += \
qgstbufferpoolinterface.cpp \
@@ -65,33 +66,16 @@ SOURCES += \
gstvideoconnector.c \
qgstreamervideoprobecontrol.cpp \
qgstreameraudioprobecontrol.cpp \
qgstreamervideowindow.cpp
config_xvideo {
DEFINES += HAVE_XVIDEO
LIBS += -lXv -lX11 -lXext
qtHaveModule(widgets) {
QT += multimediawidgets
PRIVATE_HEADERS += \
qgstxvimagebuffer_p.h \
qgstreamervideowidget_p.h
SOURCES += \
qgstxvimagebuffer.cpp \
qtHaveModule(widgets) {
QT += multimediawidgets
PRIVATE_HEADERS += \
qgstreamervideooverlay_p.h \
qgstreamervideowindow_p.h \
qgstreamervideowidget_p.h \
qx11videosurface_p.h \
SOURCES += \
qgstreamervideooverlay.cpp \
qgstreamervideowindow.cpp \
qgstreamervideowidget.cpp \
qx11videosurface.cpp \
}
qgstreamervideowidget.cpp
}
maemo6 {

View File

@@ -1,228 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qgstreamervideooverlay_p.h"
#include <private/qvideosurfacegstsink_p.h>
#include <qvideosurfaceformat.h>
#include <qx11videosurface_p.h>
QGstreamerVideoOverlay::QGstreamerVideoOverlay(QObject *parent)
: QVideoWindowControl(parent)
, m_surface(new QX11VideoSurface)
, m_videoSink(reinterpret_cast<GstElement*>(QVideoSurfaceGstSink::createSink(m_surface)))
, m_aspectRatioMode(Qt::KeepAspectRatio)
, m_fullScreen(false)
{
if (m_videoSink) {
gst_object_ref(GST_OBJECT(m_videoSink)); //Take ownership
gst_object_sink(GST_OBJECT(m_videoSink));
}
connect(m_surface, SIGNAL(surfaceFormatChanged(QVideoSurfaceFormat)),
this, SLOT(surfaceFormatChanged()));
}
QGstreamerVideoOverlay::~QGstreamerVideoOverlay()
{
if (m_videoSink)
gst_object_unref(GST_OBJECT(m_videoSink));
delete m_surface;
}
WId QGstreamerVideoOverlay::winId() const
{
return m_surface->winId();
}
void QGstreamerVideoOverlay::setWinId(WId id)
{
bool wasReady = isReady();
m_surface->setWinId(id);
if (isReady() != wasReady)
emit readyChanged(!wasReady);
}
QRect QGstreamerVideoOverlay::displayRect() const
{
return m_displayRect;
}
void QGstreamerVideoOverlay::setDisplayRect(const QRect &rect)
{
m_displayRect = rect;
setScaledDisplayRect();
}
Qt::AspectRatioMode QGstreamerVideoOverlay::aspectRatioMode() const
{
return m_aspectRatioMode;
}
void QGstreamerVideoOverlay::setAspectRatioMode(Qt::AspectRatioMode mode)
{
m_aspectRatioMode = mode;
setScaledDisplayRect();
}
void QGstreamerVideoOverlay::repaint()
{
}
int QGstreamerVideoOverlay::brightness() const
{
return m_surface->brightness();
}
void QGstreamerVideoOverlay::setBrightness(int brightness)
{
m_surface->setBrightness(brightness);
emit brightnessChanged(m_surface->brightness());
}
int QGstreamerVideoOverlay::contrast() const
{
return m_surface->contrast();
}
void QGstreamerVideoOverlay::setContrast(int contrast)
{
m_surface->setContrast(contrast);
emit contrastChanged(m_surface->contrast());
}
int QGstreamerVideoOverlay::hue() const
{
return m_surface->hue();
}
void QGstreamerVideoOverlay::setHue(int hue)
{
m_surface->setHue(hue);
emit hueChanged(m_surface->hue());
}
int QGstreamerVideoOverlay::saturation() const
{
return m_surface->saturation();
}
void QGstreamerVideoOverlay::setSaturation(int saturation)
{
m_surface->setSaturation(saturation);
emit saturationChanged(m_surface->saturation());
}
bool QGstreamerVideoOverlay::isFullScreen() const
{
return m_fullScreen;
}
void QGstreamerVideoOverlay::setFullScreen(bool fullScreen)
{
emit fullScreenChanged(m_fullScreen = fullScreen);
}
QSize QGstreamerVideoOverlay::nativeSize() const
{
return m_surface->surfaceFormat().sizeHint();
}
QAbstractVideoSurface *QGstreamerVideoOverlay::surface() const
{
return m_surface;
}
GstElement *QGstreamerVideoOverlay::videoSink()
{
return m_videoSink;
}
void QGstreamerVideoOverlay::surfaceFormatChanged()
{
setScaledDisplayRect();
emit nativeSizeChanged();
}
void QGstreamerVideoOverlay::setScaledDisplayRect()
{
QRect formatViewport = m_surface->surfaceFormat().viewport();
switch (m_aspectRatioMode) {
case Qt::KeepAspectRatio:
{
QSize size = m_surface->surfaceFormat().sizeHint();
size.scale(m_displayRect.size(), Qt::KeepAspectRatio);
QRect rect(QPoint(0, 0), size);
rect.moveCenter(m_displayRect.center());
m_surface->setDisplayRect(rect);
m_surface->setViewport(formatViewport);
}
break;
case Qt::IgnoreAspectRatio:
m_surface->setDisplayRect(m_displayRect);
m_surface->setViewport(formatViewport);
break;
case Qt::KeepAspectRatioByExpanding:
{
QSize size = m_displayRect.size();
size.scale(m_surface->surfaceFormat().sizeHint(), Qt::KeepAspectRatio);
QRect viewport(QPoint(0, 0), size);
viewport.moveCenter(formatViewport.center());
m_surface->setDisplayRect(m_displayRect);
m_surface->setViewport(viewport);
}
break;
};
}

View File

@@ -47,9 +47,6 @@
#include <QtWidgets/qapplication.h>
#include <QtGui/qpainter.h>
#ifdef Q_WS_X11
# include <X11/Xlib.h>
#endif
#include <gst/gst.h>
#include <gst/interfaces/xoverlay.h>
#include <gst/interfaces/propertyprobe.h>

View File

@@ -48,14 +48,6 @@
#include <gst/interfaces/xoverlay.h>
#include <gst/interfaces/propertyprobe.h>
/*
QGstreamerVideoWindow is similar to QGstreamerVideoOverlay,
but uses xvimagesink like gstreamer element instead of QX11VideoSurface.
This allows to use the accelerated elements if available on the target platform,
but requires at least 0.10.29 gstreamer version
with gst_x_overlay_set_render_rectangle to set display rect.
*/
QGstreamerVideoWindow::QGstreamerVideoWindow(QObject *parent, const char *elementName)
: QVideoWindowControl(parent)
@@ -95,8 +87,6 @@ void QGstreamerVideoWindow::setWinId(WId id)
if (m_windowId == id)
return;
qDebug() << Q_FUNC_INFO << id;
WId oldId = m_windowId;
m_windowId = id;

View File

@@ -1,329 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtCore/qdebug.h>
#include <QtCore/qthread.h>
#include <QtCore/qvariant.h>
#include <QtGui/qguiapplication.h>
#include <QtGui/qwindow.h>
#include <qpa/qplatformnativeinterface.h>
#include "qgstxvimagebuffer_p.h"
#include "qvideosurfacegstsink_p.h"
#include "qgstvideobuffer_p.h"
QT_BEGIN_NAMESPACE
GstBufferClass *QGstXvImageBuffer::parent_class = NULL;
GType QGstXvImageBuffer::get_type(void)
{
static GType buffer_type = 0;
if (buffer_type == 0) {
static const GTypeInfo buffer_info = {
sizeof (GstBufferClass),
NULL,
NULL,
QGstXvImageBuffer::class_init,
NULL,
NULL,
sizeof(QGstXvImageBuffer),
0,
(GInstanceInitFunc)QGstXvImageBuffer::buffer_init,
NULL
};
buffer_type = g_type_register_static(GST_TYPE_BUFFER,
"QGstXvImageBuffer", &buffer_info, GTypeFlags(0));
}
return buffer_type;
}
void QGstXvImageBuffer::class_init(gpointer g_class, gpointer class_data)
{
Q_UNUSED(class_data);
GST_MINI_OBJECT_CLASS(g_class)->finalize =
(GstMiniObjectFinalizeFunction)buffer_finalize;
parent_class = (GstBufferClass*)g_type_class_peek_parent(g_class);
}
void QGstXvImageBuffer::buffer_init(QGstXvImageBuffer *xvImage, gpointer g_class)
{
Q_UNUSED(g_class);
xvImage->pool = 0;
xvImage->shmInfo.shmaddr = ((char *) -1);
xvImage->shmInfo.shmid = -1;
xvImage->markedForDeletion = false;
}
void QGstXvImageBuffer::buffer_finalize(QGstXvImageBuffer * xvImage)
{
if (xvImage->pool) {
if (xvImage->markedForDeletion)
xvImage->pool->destroyBuffer(xvImage);
else
xvImage->pool->recycleBuffer(xvImage);
}
}
QGstXvImageBufferPool::QGstXvImageBufferPool(QObject *parent)
:QObject(parent)
{
m_threadId = QThread::currentThreadId();
}
QGstXvImageBufferPool::~QGstXvImageBufferPool()
{
}
bool QGstXvImageBufferPool::isFormatSupported(const QVideoSurfaceFormat &surfaceFormat) const
{
bool ok = true;
surfaceFormat.property("portId").toULongLong(&ok);
if (!ok)
return false;
int xvFormatId = surfaceFormat.property("xvFormatId").toInt(&ok);
if (!ok || xvFormatId < 0)
return false;
int dataSize = surfaceFormat.property("dataSize").toInt(&ok);
if (!ok || dataSize<=0)
return false;
return true;
}
GType QGstXvImageBufferPool::bufferType() const
{
return QGstXvImageBuffer::get_type();
}
GstBuffer *QGstXvImageBufferPool::takeBuffer(
const QVideoSurfaceFormat &format, GstCaps *caps)
{
m_poolMutex.lock();
m_caps = caps;
if (format != m_format) {
doClear();
m_format = format;
}
if (m_pool.isEmpty()) {
//qDebug() << "QGstXvImageBufferPool::takeBuffer: no buffer available, allocate the new one" << QThread::currentThreadId() << m_threadId;
if (QThread::currentThreadId() == m_threadId) {
doAlloc();
} else {
QMetaObject::invokeMethod(this, "queuedAlloc", Qt::QueuedConnection);
m_allocWaitCondition.wait(&m_poolMutex, 300);
}
}
QGstXvImageBuffer *res = 0;
if (!m_pool.isEmpty()) {
res = m_pool.takeLast();
}
m_poolMutex.unlock();
return GST_BUFFER(res);
}
QAbstractVideoBuffer::HandleType QGstXvImageBufferPool::handleType() const
{
return QAbstractVideoBuffer::XvShmImageHandle;
}
QAbstractVideoBuffer *QGstXvImageBufferPool::prepareVideoBuffer(GstBuffer *buffer, int bytesPerLine)
{
if (!G_TYPE_CHECK_INSTANCE_TYPE(buffer, bufferType()))
return 0;
QGstXvImageBuffer *xvBuffer = reinterpret_cast<QGstXvImageBuffer *>(buffer);
QVariant handle = QVariant::fromValue(xvBuffer->xvImage);
return new QGstVideoBuffer(buffer, bytesPerLine, QAbstractVideoBuffer::XvShmImageHandle, handle);
}
QStringList QGstXvImageBufferPool::keys() const
{
return QStringList() << QGstBufferPoolPluginKey;
}
void QGstXvImageBufferPool::queuedAlloc()
{
QMutexLocker lock(&m_poolMutex);
doAlloc();
m_allocWaitCondition.wakeOne();
}
void QGstXvImageBufferPool::doAlloc()
{
//should be always called from the main thread with m_poolMutex locked
//Q_ASSERT(QThread::currentThread() == thread());
XSync(display(), false);
QGstXvImageBuffer *xvBuffer = (QGstXvImageBuffer *)gst_mini_object_new(QGstXvImageBuffer::get_type());
quint64 portId = m_format.property("portId").toULongLong();
int xvFormatId = m_format.property("xvFormatId").toInt();
xvBuffer->xvImage = XvShmCreateImage(
display(),
portId,
xvFormatId,
0,
m_format.frameWidth(),
m_format.frameHeight(),
&xvBuffer->shmInfo
);
if (!xvBuffer->xvImage) {
qWarning() << "QGstXvImageBufferPool: XvShmCreateImage failed";
return;
}
XSync(display(), false);
xvBuffer->shmInfo.shmid = shmget(IPC_PRIVATE, xvBuffer->xvImage->data_size, IPC_CREAT | 0777);
xvBuffer->shmInfo.shmaddr = xvBuffer->xvImage->data = (char*)shmat(xvBuffer->shmInfo.shmid, 0, 0);
xvBuffer->shmInfo.readOnly = False;
if (!XShmAttach(display(), &xvBuffer->shmInfo)) {
qWarning() << "QGstXvImageBufferPool: XShmAttach failed";
return;
}
XSync(display(), false);
shmctl (xvBuffer->shmInfo.shmid, IPC_RMID, NULL);
xvBuffer->pool = this;
GST_MINI_OBJECT_CAST(xvBuffer)->flags = 0;
gst_buffer_set_caps(GST_BUFFER_CAST(xvBuffer), m_caps);
GST_BUFFER_DATA(xvBuffer) = (uchar*)xvBuffer->xvImage->data;
GST_BUFFER_SIZE(xvBuffer) = xvBuffer->xvImage->data_size;
m_allBuffers.append(xvBuffer);
m_pool.append(xvBuffer);
XSync(display(), false);
}
void QGstXvImageBufferPool::clear()
{
QMutexLocker lock(&m_poolMutex);
doClear();
}
void QGstXvImageBufferPool::doClear()
{
foreach (QGstXvImageBuffer *xvBuffer, m_allBuffers) {
xvBuffer->markedForDeletion = true;
}
m_allBuffers.clear();
foreach (QGstXvImageBuffer *xvBuffer, m_pool) {
gst_buffer_unref(GST_BUFFER(xvBuffer));
}
m_pool.clear();
m_format = QVideoSurfaceFormat();
}
void QGstXvImageBufferPool::queuedDestroy()
{
QMutexLocker lock(&m_destroyMutex);
XSync(display(), false);
foreach(XvShmImage xvImage, m_imagesToDestroy) {
if (xvImage.shmInfo.shmaddr != ((void *) -1)) {
XShmDetach(display(), &xvImage.shmInfo);
XSync(display(), false);
shmdt(xvImage.shmInfo.shmaddr);
}
if (xvImage.xvImage)
XFree(xvImage.xvImage);
}
m_imagesToDestroy.clear();
XSync(display(), false);
}
void QGstXvImageBufferPool::recycleBuffer(QGstXvImageBuffer *xvBuffer)
{
QMutexLocker lock(&m_poolMutex);
gst_buffer_ref(GST_BUFFER_CAST(xvBuffer));
m_pool.append(xvBuffer);
}
void QGstXvImageBufferPool::destroyBuffer(QGstXvImageBuffer *xvBuffer)
{
XvShmImage imageToDestroy;
imageToDestroy.xvImage = xvBuffer->xvImage;
imageToDestroy.shmInfo = xvBuffer->shmInfo;
m_destroyMutex.lock();
m_imagesToDestroy.append(imageToDestroy);
m_destroyMutex.unlock();
if (m_imagesToDestroy.size() == 1)
QMetaObject::invokeMethod(this, "queuedDestroy", Qt::QueuedConnection);
}
Display *QGstXvImageBufferPool::display() const
{
QWindow *window = QGuiApplication::topLevelWindows().first();
Display *display = static_cast<Display *>(QGuiApplication::platformNativeInterface()->nativeResourceForWindow("Display", window));
return display;
}
QT_END_NAMESPACE

View File

@@ -49,10 +49,6 @@
#include <private/qmediapluginloader_p.h>
#include "qgstvideobuffer_p.h"
#if defined(HAVE_XVIDEO)
#include "qgstxvimagebuffer_p.h"
#endif
#include "qvideosurfacegstsink_p.h"
//#define DEBUG_VIDEO_SURFACE_SINK
@@ -79,9 +75,6 @@ QVideoSurfaceGstDelegate::QVideoSurfaceGstDelegate(
m_pools.append(plugin);
}
}
#ifdef HAVE_XVIDEO
m_pools.append(new QGstXvImageBufferPool(this));
#endif
updateSupportedFormats();
connect(m_surface, SIGNAL(supportedFormatsChanged()), this, SLOT(updateSupportedFormats()));
}

View File

@@ -1,534 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtCore/qvariant.h>
#include <QtCore/qdebug.h>
#include <QtGui/qguiapplication.h>
#include <qpa/qplatformnativeinterface.h>
#include <qvideosurfaceformat.h>
#include "qx11videosurface_p.h"
Q_DECLARE_METATYPE(XvImage*);
struct XvFormatRgb
{
QVideoFrame::PixelFormat pixelFormat;
int bits_per_pixel;
int format;
int num_planes;
int depth;
unsigned int red_mask;
unsigned int green_mask;
unsigned int blue_mask;
};
bool operator ==(const XvImageFormatValues &format, const XvFormatRgb &rgb)
{
return format.type == XvRGB
&& format.bits_per_pixel == rgb.bits_per_pixel
&& format.format == rgb.format
&& format.num_planes == rgb.num_planes
&& format.depth == rgb.depth
&& format.red_mask == rgb.red_mask
&& format.blue_mask == rgb.blue_mask;
}
static const XvFormatRgb qt_xvRgbLookup[] =
{
{ QVideoFrame::Format_ARGB32, 32, XvPacked, 1, 32, 0x00FF0000, 0x0000FF00, 0x000000FF },
{ QVideoFrame::Format_RGB32 , 32, XvPacked, 1, 24, 0x00FF0000, 0x0000FF00, 0x000000FF },
{ QVideoFrame::Format_RGB24 , 24, XvPacked, 1, 24, 0x00FF0000, 0x0000FF00, 0x000000FF },
{ QVideoFrame::Format_RGB565, 16, XvPacked, 1, 16, 0x0000F800, 0x000007E0, 0x0000001F },
{ QVideoFrame::Format_BGRA32, 32, XvPacked, 1, 32, 0xFF000000, 0x00FF0000, 0x0000FF00 },
{ QVideoFrame::Format_BGR32 , 32, XvPacked, 1, 24, 0x00FF0000, 0x0000FF00, 0x000000FF },
{ QVideoFrame::Format_BGR24 , 24, XvPacked, 1, 24, 0x00FF0000, 0x0000FF00, 0x000000FF },
{ QVideoFrame::Format_BGR565, 16, XvPacked, 1, 16, 0x0000F800, 0x000007E0, 0x0000001F }
};
struct XvFormatYuv
{
QVideoFrame::PixelFormat pixelFormat;
int bits_per_pixel;
int format;
int num_planes;
unsigned int y_sample_bits;
unsigned int u_sample_bits;
unsigned int v_sample_bits;
unsigned int horz_y_period;
unsigned int horz_u_period;
unsigned int horz_v_period;
unsigned int vert_y_period;
unsigned int vert_u_period;
unsigned int vert_v_period;
char component_order[32];
};
bool operator ==(const XvImageFormatValues &format, const XvFormatYuv &yuv)
{
return format.type == XvYUV
&& format.bits_per_pixel == yuv.bits_per_pixel
&& format.format == yuv.format
&& format.num_planes == yuv.num_planes
&& format.y_sample_bits == yuv.y_sample_bits
&& format.u_sample_bits == yuv.u_sample_bits
&& format.v_sample_bits == yuv.v_sample_bits
&& format.horz_y_period == yuv.horz_y_period
&& format.horz_u_period == yuv.horz_u_period
&& format.horz_v_period == yuv.horz_v_period
&& format.horz_y_period == yuv.vert_y_period
&& format.vert_u_period == yuv.vert_u_period
&& format.vert_v_period == yuv.vert_v_period
&& qstrncmp(format.component_order, yuv.component_order, 32) == 0;
}
static const XvFormatYuv qt_xvYuvLookup[] =
{
{ QVideoFrame::Format_YUV444 , 24, XvPacked, 1, 8, 8, 8, 1, 1, 1, 1, 1, 1, "YUV" },
{ QVideoFrame::Format_YUV420P, 12, XvPlanar, 3, 8, 8, 8, 1, 2, 2, 1, 2, 2, "YUV" },
{ QVideoFrame::Format_YV12 , 12, XvPlanar, 3, 8, 8, 8, 1, 2, 2, 1, 2, 2, "YVU" },
{ QVideoFrame::Format_UYVY , 16, XvPacked, 1, 8, 8, 8, 1, 2, 2, 1, 1, 1, "UYVY" },
{ QVideoFrame::Format_YUYV , 16, XvPacked, 1, 8, 8, 8, 1, 2, 2, 1, 1, 1, "YUY2" },
{ QVideoFrame::Format_YUYV , 16, XvPacked, 1, 8, 8, 8, 1, 2, 2, 1, 1, 1, "YUYV" },
{ QVideoFrame::Format_NV12 , 12, XvPlanar, 2, 8, 8, 8, 1, 2, 2, 1, 2, 2, "YUV" },
{ QVideoFrame::Format_NV12 , 12, XvPlanar, 2, 8, 8, 8, 1, 2, 2, 1, 2, 2, "YVU" },
{ QVideoFrame::Format_Y8 , 8 , XvPlanar, 1, 8, 0, 0, 1, 0, 0, 1, 0, 0, "Y" }
};
QX11VideoSurface::QX11VideoSurface(QObject *parent)
: QAbstractVideoSurface(parent)
, m_winId(0)
, m_portId(0)
, m_gc(0)
, m_image(0)
{
}
QX11VideoSurface::~QX11VideoSurface()
{
if (m_gc)
XFreeGC(display(), m_gc);
if (m_portId != 0)
XvUngrabPort(display(), m_portId, 0);
}
WId QX11VideoSurface::winId() const
{
return m_winId;
}
void QX11VideoSurface::setWinId(WId id)
{
//qDebug() << "setWinID:" << id;
if (id == m_winId)
return;
if (m_image)
XFree(m_image);
if (m_gc) {
XFreeGC(display(), m_gc);
m_gc = 0;
}
if (m_portId != 0)
XvUngrabPort(display(), m_portId, 0);
m_supportedPixelFormats.clear();
m_formatIds.clear();
m_winId = id;
if (m_winId && findPort()) {
querySupportedFormats();
m_gc = XCreateGC(display(), m_winId, 0, 0);
if (m_image) {
m_image = 0;
if (!start(surfaceFormat())) {
QAbstractVideoSurface::stop();
qWarning() << "Failed to start video surface with format" << surfaceFormat();
}
}
} else {
qWarning() << "Failed to find XVideo port";
if (m_image) {
m_image = 0;
QAbstractVideoSurface::stop();
}
}
emit supportedFormatsChanged();
}
QRect QX11VideoSurface::displayRect() const
{
return m_displayRect;
}
void QX11VideoSurface::setDisplayRect(const QRect &rect)
{
m_displayRect = rect;
}
QRect QX11VideoSurface::viewport() const
{
return m_viewport;
}
void QX11VideoSurface::setViewport(const QRect &rect)
{
m_viewport = rect;
}
int QX11VideoSurface::brightness() const
{
return getAttribute("XV_BRIGHTNESS", m_brightnessRange.first, m_brightnessRange.second);
}
void QX11VideoSurface::setBrightness(int brightness)
{
setAttribute("XV_BRIGHTNESS", brightness, m_brightnessRange.first, m_brightnessRange.second);
}
int QX11VideoSurface::contrast() const
{
return getAttribute("XV_CONTRAST", m_contrastRange.first, m_contrastRange.second);
}
void QX11VideoSurface::setContrast(int contrast)
{
setAttribute("XV_CONTRAST", contrast, m_contrastRange.first, m_contrastRange.second);
}
int QX11VideoSurface::hue() const
{
return getAttribute("XV_HUE", m_hueRange.first, m_hueRange.second);
}
void QX11VideoSurface::setHue(int hue)
{
setAttribute("XV_HUE", hue, m_hueRange.first, m_hueRange.second);
}
int QX11VideoSurface::saturation() const
{
return getAttribute("XV_SATURATION", m_saturationRange.first, m_saturationRange.second);
}
void QX11VideoSurface::setSaturation(int saturation)
{
setAttribute("XV_SATURATION", saturation, m_saturationRange.first, m_saturationRange.second);
}
int QX11VideoSurface::getAttribute(const char *attribute, int minimum, int maximum) const
{
if (m_portId != 0) {
Display *disp = display();
Atom atom = XInternAtom(disp, attribute, True);
int value = 0;
XvGetPortAttribute(disp, m_portId, atom, &value);
return redistribute(value, minimum, maximum, -100, 100);
} else {
return 0;
}
}
void QX11VideoSurface::setAttribute(const char *attribute, int value, int minimum, int maximum)
{
if (m_portId != 0) {
Display *disp = display();
Atom atom = XInternAtom(disp, attribute, True);
XvSetPortAttribute(
disp, m_portId, atom, redistribute(value, -100, 100, minimum, maximum));
}
}
int QX11VideoSurface::redistribute(
int value, int fromLower, int fromUpper, int toLower, int toUpper)
{
return fromUpper != fromLower
? ((value - fromLower) * (toUpper - toLower) / (fromUpper - fromLower)) + toLower
: 0;
}
QList<QVideoFrame::PixelFormat> QX11VideoSurface::supportedPixelFormats(
QAbstractVideoBuffer::HandleType handleType) const
{
return handleType == QAbstractVideoBuffer::NoHandle || handleType == QAbstractVideoBuffer::XvShmImageHandle
? m_supportedPixelFormats
: QList<QVideoFrame::PixelFormat>();
}
bool QX11VideoSurface::start(const QVideoSurfaceFormat &format)
{
if (m_image)
XFree(m_image);
int xvFormatId = 0;
for (int i = 0; i < m_supportedPixelFormats.count(); ++i) {
if (m_supportedPixelFormats.at(i) == format.pixelFormat()) {
xvFormatId = m_formatIds.at(i);
break;
}
}
if (xvFormatId == 0) {
setError(UnsupportedFormatError);
} else {
XvImage *image = XvCreateImage(
display(),
m_portId,
xvFormatId,
0,
format.frameWidth(),
format.frameHeight());
if (!image) {
setError(ResourceError);
} else {
m_viewport = format.viewport();
m_image = image;
QVideoSurfaceFormat newFormat = format;
newFormat.setProperty("portId", QVariant(quint64(m_portId)));
newFormat.setProperty("xvFormatId", xvFormatId);
newFormat.setProperty("dataSize", image->data_size);
return QAbstractVideoSurface::start(newFormat);
}
}
if (m_image) {
m_image = 0;
QAbstractVideoSurface::stop();
}
return false;
}
void QX11VideoSurface::stop()
{
if (m_image) {
XFree(m_image);
m_image = 0;
QAbstractVideoSurface::stop();
}
}
bool QX11VideoSurface::present(const QVideoFrame &frame)
{
if (!m_image) {
setError(StoppedError);
return false;
} else if (m_image->width != frame.width() || m_image->height != frame.height()) {
setError(IncorrectFormatError);
return false;
} else {
QVideoFrame frameCopy(frame);
if (!frameCopy.map(QAbstractVideoBuffer::ReadOnly)) {
setError(IncorrectFormatError);
return false;
} else {
bool presented = false;
if (frame.handleType() != QAbstractVideoBuffer::XvShmImageHandle &&
m_image->data_size > frame.mappedBytes()) {
qWarning("Insufficient frame buffer size");
setError(IncorrectFormatError);
} else if (frame.handleType() != QAbstractVideoBuffer::XvShmImageHandle &&
m_image->num_planes > 0 &&
m_image->pitches[0] != frame.bytesPerLine()) {
qWarning("Incompatible frame pitches");
setError(IncorrectFormatError);
} else {
if (frame.handleType() != QAbstractVideoBuffer::XvShmImageHandle) {
m_image->data = reinterpret_cast<char *>(frameCopy.bits());
//qDebug() << "copy frame";
XvPutImage(
display(),
m_portId,
m_winId,
m_gc,
m_image,
m_viewport.x(),
m_viewport.y(),
m_viewport.width(),
m_viewport.height(),
m_displayRect.x(),
m_displayRect.y(),
m_displayRect.width(),
m_displayRect.height());
m_image->data = 0;
} else {
XvImage *img = frame.handle().value<XvImage*>();
//qDebug() << "render directly";
if (img)
XvShmPutImage(
display(),
m_portId,
m_winId,
m_gc,
img,
m_viewport.x(),
m_viewport.y(),
m_viewport.width(),
m_viewport.height(),
m_displayRect.x(),
m_displayRect.y(),
m_displayRect.width(),
m_displayRect.height(),
false);
}
presented = true;
}
frameCopy.unmap();
return presented;
}
}
}
Display *QX11VideoSurface::display() const
{
QWindow *window = QGuiApplication::focusWindow();
Display *display = (Display *)QGuiApplication::platformNativeInterface()->nativeResourceForWindow("Display", window);
return display;
}
bool QX11VideoSurface::findPort()
{
unsigned int count = 0;
XvAdaptorInfo *adaptors = 0;
bool portFound = false;
if (XvQueryAdaptors(display(), m_winId, &count, &adaptors) == Success) {
for (unsigned int i = 0; i < count && !portFound; ++i) {
if (adaptors[i].type & XvImageMask) {
m_portId = adaptors[i].base_id;
for (unsigned int j = 0; j < adaptors[i].num_ports && !portFound; ++j, ++m_portId)
portFound = XvGrabPort(display(), m_portId, 0) == Success;
}
}
XvFreeAdaptorInfo(adaptors);
}
return portFound;
}
void QX11VideoSurface::querySupportedFormats()
{
int count = 0;
if (XvImageFormatValues *imageFormats = XvListImageFormats(
display(), m_portId, &count)) {
const int rgbCount = sizeof(qt_xvRgbLookup) / sizeof(XvFormatRgb);
const int yuvCount = sizeof(qt_xvYuvLookup) / sizeof(XvFormatYuv);
for (int i = 0; i < count; ++i) {
switch (imageFormats[i].type) {
case XvRGB:
for (int j = 0; j < rgbCount; ++j) {
if (imageFormats[i] == qt_xvRgbLookup[j]) {
m_supportedPixelFormats.append(qt_xvRgbLookup[j].pixelFormat);
m_formatIds.append(imageFormats[i].id);
break;
}
}
break;
case XvYUV:
for (int j = 0; j < yuvCount; ++j) {
if (imageFormats[i] == qt_xvYuvLookup[j]) {
m_supportedPixelFormats.append(qt_xvYuvLookup[j].pixelFormat);
m_formatIds.append(imageFormats[i].id);
break;
}
}
break;
}
}
XFree(imageFormats);
}
m_brightnessRange = qMakePair(0, 0);
m_contrastRange = qMakePair(0, 0);
m_hueRange = qMakePair(0, 0);
m_saturationRange = qMakePair(0, 0);
if (XvAttribute *attributes = XvQueryPortAttributes(display(), m_portId, &count)) {
for (int i = 0; i < count; ++i) {
if (qstrcmp(attributes[i].name, "XV_BRIGHTNESS") == 0)
m_brightnessRange = qMakePair(attributes[i].min_value, attributes[i].max_value);
else if (qstrcmp(attributes[i].name, "XV_CONTRAST") == 0)
m_contrastRange = qMakePair(attributes[i].min_value, attributes[i].max_value);
else if (qstrcmp(attributes[i].name, "XV_HUE") == 0)
m_hueRange = qMakePair(attributes[i].min_value, attributes[i].max_value);
else if (qstrcmp(attributes[i].name, "XV_SATURATION") == 0)
m_saturationRange = qMakePair(attributes[i].min_value, attributes[i].max_value);
}
XFree(attributes);
}
}