plugins/multimedia/gstreamer/qx11videosurface.cpp
changeset 0 876b1a06bc25
child 5 603d3f8b6302
equal deleted inserted replaced
-1:000000000000 0:876b1a06bc25
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Mobility Components.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include <QtCore/qvariant.h>
       
    43 #include <QtCore/qdebug.h>
       
    44 #include <QtGui/qx11info_x11.h>
       
    45 #include <qvideosurfaceformat.h>
       
    46 
       
    47 #ifndef QT_NO_XVIDEO
       
    48 
       
    49 #include "qx11videosurface.h"
       
    50 
       
    51 Q_DECLARE_METATYPE(XvImage*);
       
    52 
       
    53 struct XvFormatRgb
       
    54 {
       
    55     QVideoFrame::PixelFormat pixelFormat;
       
    56     int bits_per_pixel;
       
    57     int format;
       
    58     int num_planes;
       
    59 
       
    60     int depth;
       
    61     unsigned int red_mask;
       
    62     unsigned int green_mask;
       
    63     unsigned int blue_mask;
       
    64 
       
    65 };
       
    66 
       
    67 bool operator ==(const XvImageFormatValues &format, const XvFormatRgb &rgb)
       
    68 {
       
    69     return format.type == XvRGB
       
    70             && format.bits_per_pixel == rgb.bits_per_pixel
       
    71             && format.format         == rgb.format
       
    72             && format.num_planes     == rgb.num_planes
       
    73             && format.depth          == rgb.depth
       
    74             && format.red_mask       == rgb.red_mask
       
    75             && format.blue_mask      == rgb.blue_mask;
       
    76 }
       
    77 
       
    78 static const XvFormatRgb qt_xvRgbLookup[] =
       
    79 {
       
    80     { QVideoFrame::Format_ARGB32, 32, XvPacked, 1, 32, 0x00FF0000, 0x0000FF00, 0x000000FF },
       
    81     { QVideoFrame::Format_RGB32 , 32, XvPacked, 1, 24, 0x00FF0000, 0x0000FF00, 0x000000FF },
       
    82     { QVideoFrame::Format_RGB24 , 24, XvPacked, 1, 24, 0x00FF0000, 0x0000FF00, 0x000000FF },
       
    83     { QVideoFrame::Format_RGB565, 16, XvPacked, 1, 16, 0x0000F800, 0x000007E0, 0x0000001F },
       
    84     { QVideoFrame::Format_BGRA32, 32, XvPacked, 1, 32, 0xFF000000, 0x00FF0000, 0x0000FF00 },
       
    85     { QVideoFrame::Format_BGR32 , 32, XvPacked, 1, 24, 0x00FF0000, 0x0000FF00, 0x000000FF },
       
    86     { QVideoFrame::Format_BGR24 , 24, XvPacked, 1, 24, 0x00FF0000, 0x0000FF00, 0x000000FF },
       
    87     { QVideoFrame::Format_BGR565, 16, XvPacked, 1, 16, 0x0000F800, 0x000007E0, 0x0000001F }
       
    88 };
       
    89 
       
    90 struct XvFormatYuv
       
    91 {
       
    92     QVideoFrame::PixelFormat pixelFormat;
       
    93     int bits_per_pixel;
       
    94     int format;
       
    95     int num_planes;
       
    96 
       
    97     unsigned int y_sample_bits;
       
    98     unsigned int u_sample_bits;
       
    99     unsigned int v_sample_bits;
       
   100     unsigned int horz_y_period;
       
   101     unsigned int horz_u_period;
       
   102     unsigned int horz_v_period;
       
   103     unsigned int vert_y_period;
       
   104     unsigned int vert_u_period;
       
   105     unsigned int vert_v_period;
       
   106     char component_order[32];
       
   107 };
       
   108 
       
   109 bool operator ==(const XvImageFormatValues &format, const XvFormatYuv &yuv)
       
   110 {
       
   111     return format.type == XvYUV
       
   112             && format.bits_per_pixel == yuv.bits_per_pixel
       
   113             && format.format         == yuv.format
       
   114             && format.num_planes     == yuv.num_planes
       
   115             && format.y_sample_bits  == yuv.y_sample_bits
       
   116             && format.u_sample_bits  == yuv.u_sample_bits
       
   117             && format.v_sample_bits  == yuv.v_sample_bits
       
   118             && format.horz_y_period  == yuv.horz_y_period
       
   119             && format.horz_u_period  == yuv.horz_u_period
       
   120             && format.horz_v_period  == yuv.horz_v_period
       
   121             && format.horz_y_period  == yuv.vert_y_period
       
   122             && format.vert_u_period  == yuv.vert_u_period
       
   123             && format.vert_v_period  == yuv.vert_v_period
       
   124             && qstrncmp(format.component_order, yuv.component_order, 32) == 0;
       
   125 }
       
   126 
       
   127 static const XvFormatYuv qt_xvYuvLookup[] =
       
   128 {
       
   129     { QVideoFrame::Format_YUV444 , 24, XvPacked, 1, 8, 8, 8, 1, 1, 1, 1, 1, 1, "YUV"  },
       
   130     { QVideoFrame::Format_YUV420P, 12, XvPlanar, 3, 8, 8, 8, 1, 2, 2, 1, 2, 2, "YUV"  },
       
   131     { QVideoFrame::Format_YV12   , 12, XvPlanar, 3, 8, 8, 8, 1, 2, 2, 1, 2, 2, "YVU"  },
       
   132     { QVideoFrame::Format_UYVY   , 16, XvPacked, 1, 8, 8, 8, 1, 2, 2, 1, 1, 1, "UYVY" },
       
   133     { QVideoFrame::Format_YUYV   , 16, XvPacked, 1, 8, 8, 8, 1, 2, 2, 1, 1, 1, "YUY2" },
       
   134     { QVideoFrame::Format_YUYV   , 16, XvPacked, 1, 8, 8, 8, 1, 2, 2, 1, 1, 1, "YUYV" },
       
   135     { QVideoFrame::Format_NV12   , 12, XvPlanar, 2, 8, 8, 8, 1, 2, 2, 1, 2, 2, "YUV"  },
       
   136     { QVideoFrame::Format_NV12   , 12, XvPlanar, 2, 8, 8, 8, 1, 2, 2, 1, 2, 2, "YVU"  },
       
   137     { QVideoFrame::Format_Y8     , 8 , XvPlanar, 1, 8, 0, 0, 1, 0, 0, 1, 0, 0, "Y"    }
       
   138 };
       
   139 
       
   140 QX11VideoSurface::QX11VideoSurface(QObject *parent)
       
   141     : QAbstractVideoSurface(parent)
       
   142     , m_winId(0)
       
   143     , m_portId(0)
       
   144     , m_gc(0)
       
   145     , m_image(0)
       
   146 {
       
   147 }
       
   148 
       
   149 QX11VideoSurface::~QX11VideoSurface()
       
   150 {
       
   151     if (m_gc)
       
   152         XFreeGC(QX11Info::display(), m_gc);
       
   153 
       
   154     if (m_portId != 0)
       
   155         XvUngrabPort(QX11Info::display(), m_portId, 0);
       
   156 }
       
   157 
       
   158 WId QX11VideoSurface::winId() const
       
   159 {
       
   160     return m_winId;
       
   161 }
       
   162 
       
   163 void QX11VideoSurface::setWinId(WId id)
       
   164 {
       
   165     //qDebug() << "setWinID:" << id;
       
   166 
       
   167     if (id == m_winId)
       
   168         return;
       
   169 
       
   170     if (m_image)
       
   171         XFree(m_image);
       
   172 
       
   173     if (m_gc) {
       
   174         XFreeGC(QX11Info::display(), m_gc);
       
   175         m_gc = 0;
       
   176     }
       
   177 
       
   178     if (m_portId != 0)
       
   179         XvUngrabPort(QX11Info::display(), m_portId, 0);
       
   180 
       
   181     m_supportedPixelFormats.clear();
       
   182     m_formatIds.clear();
       
   183 
       
   184     m_winId = id;
       
   185 
       
   186     if (m_winId && findPort()) {
       
   187         querySupportedFormats();
       
   188 
       
   189         m_gc = XCreateGC(QX11Info::display(), m_winId, 0, 0);
       
   190 
       
   191         if (m_image) {
       
   192             m_image = 0;
       
   193 
       
   194             if (!start(surfaceFormat())) {
       
   195                 QAbstractVideoSurface::stop();
       
   196                 qWarning() << "Failed to start video surface with format" << surfaceFormat();
       
   197             }
       
   198         }
       
   199     } else {
       
   200         qWarning() << "Failed to find XVideo port";
       
   201         if (m_image) {
       
   202             m_image = 0;
       
   203 
       
   204             QAbstractVideoSurface::stop();
       
   205         }
       
   206     }
       
   207 
       
   208     emit supportedFormatsChanged();
       
   209 }
       
   210 
       
   211 QRect QX11VideoSurface::displayRect() const
       
   212 {
       
   213     return m_displayRect;
       
   214 }
       
   215 
       
   216 void QX11VideoSurface::setDisplayRect(const QRect &rect)
       
   217 {
       
   218     m_displayRect = rect;
       
   219 }
       
   220 
       
   221 QRect QX11VideoSurface::viewport() const
       
   222 {
       
   223     return m_viewport;
       
   224 }
       
   225 
       
   226 void QX11VideoSurface::setViewport(const QRect &rect)
       
   227 {
       
   228     m_viewport = rect;
       
   229 }
       
   230 
       
   231 int QX11VideoSurface::brightness() const
       
   232 {
       
   233     return getAttribute("XV_BRIGHTNESS", m_brightnessRange.first, m_brightnessRange.second);
       
   234 }
       
   235 
       
   236 void QX11VideoSurface::setBrightness(int brightness)
       
   237 {
       
   238     setAttribute("XV_BRIGHTNESS", brightness, m_brightnessRange.first, m_brightnessRange.second);
       
   239 }
       
   240 
       
   241 int QX11VideoSurface::contrast() const
       
   242 {
       
   243     return getAttribute("XV_CONTRAST", m_contrastRange.first, m_contrastRange.second);
       
   244 }
       
   245 
       
   246 void QX11VideoSurface::setContrast(int contrast)
       
   247 {
       
   248     setAttribute("XV_CONTRAST", contrast, m_contrastRange.first, m_contrastRange.second);
       
   249 }
       
   250 
       
   251 int QX11VideoSurface::hue() const
       
   252 {
       
   253     return getAttribute("XV_HUE", m_hueRange.first, m_hueRange.second);
       
   254 }
       
   255 
       
   256 void QX11VideoSurface::setHue(int hue)
       
   257 {
       
   258     setAttribute("XV_HUE", hue, m_hueRange.first, m_hueRange.second);
       
   259 }
       
   260 
       
   261 int QX11VideoSurface::saturation() const
       
   262 {
       
   263     return getAttribute("XV_SATURATION", m_saturationRange.first, m_saturationRange.second);
       
   264 }
       
   265 
       
   266 void QX11VideoSurface::setSaturation(int saturation)
       
   267 {
       
   268     setAttribute("XV_SATURATION", saturation, m_saturationRange.first, m_saturationRange.second);
       
   269 }
       
   270 
       
   271 int QX11VideoSurface::getAttribute(const char *attribute, int minimum, int maximum) const
       
   272 {
       
   273     if (m_portId != 0) {
       
   274         Display *display = QX11Info::display();
       
   275 
       
   276         Atom atom = XInternAtom(display, attribute, True);
       
   277 
       
   278         int value = 0;
       
   279 
       
   280         XvGetPortAttribute(display, m_portId, atom, &value);
       
   281 
       
   282         return redistribute(value, minimum, maximum, -100, 100);
       
   283     } else {
       
   284         return 0;
       
   285     }
       
   286 }
       
   287 
       
   288 void QX11VideoSurface::setAttribute(const char *attribute, int value, int minimum, int maximum)
       
   289 {
       
   290     if (m_portId != 0) {
       
   291         Display *display = QX11Info::display();
       
   292 
       
   293         Atom atom = XInternAtom(display, attribute, True);
       
   294 
       
   295         XvSetPortAttribute(
       
   296                 display, m_portId, atom, redistribute(value, -100, 100, minimum, maximum));
       
   297     }
       
   298 }
       
   299 
       
   300 int QX11VideoSurface::redistribute(
       
   301         int value, int fromLower, int fromUpper, int toLower, int toUpper)
       
   302 {
       
   303     return fromUpper != fromLower
       
   304             ? ((value - fromLower) * (toUpper - toLower) / (fromUpper - fromLower)) + toLower
       
   305             : 0;
       
   306 }
       
   307 
       
   308 QList<QVideoFrame::PixelFormat> QX11VideoSurface::supportedPixelFormats(
       
   309         QAbstractVideoBuffer::HandleType handleType) const
       
   310 {
       
   311     return handleType == QAbstractVideoBuffer::NoHandle || handleType ==  QAbstractVideoBuffer::XvShmImageHandle
       
   312             ? m_supportedPixelFormats
       
   313             : QList<QVideoFrame::PixelFormat>();
       
   314 }
       
   315 
       
   316 bool QX11VideoSurface::start(const QVideoSurfaceFormat &format)
       
   317 {
       
   318     if (m_image)
       
   319         XFree(m_image);
       
   320 
       
   321     int xvFormatId = 0;
       
   322     for (int i = 0; i < m_supportedPixelFormats.count(); ++i) {
       
   323         if (m_supportedPixelFormats.at(i) == format.pixelFormat()) {
       
   324             xvFormatId = m_formatIds.at(i);
       
   325             break;
       
   326         }
       
   327     }
       
   328 
       
   329     if (xvFormatId == 0) {
       
   330         setError(UnsupportedFormatError);
       
   331     } else {
       
   332         XvImage *image = XvCreateImage(
       
   333                 QX11Info::display(),
       
   334                 m_portId,
       
   335                 xvFormatId,
       
   336                 0,
       
   337                 format.frameWidth(),
       
   338                 format.frameHeight());
       
   339 
       
   340         if (!image) {
       
   341             setError(ResourceError);
       
   342         } else {
       
   343             m_viewport = format.viewport();
       
   344             m_image = image;
       
   345 
       
   346             QVideoSurfaceFormat newFormat = format;
       
   347             newFormat.setProperty("portId", QVariant(quint64(m_portId)));
       
   348             newFormat.setProperty("xvFormatId", xvFormatId);
       
   349             newFormat.setProperty("dataSize", image->data_size);
       
   350 
       
   351             return QAbstractVideoSurface::start(newFormat);
       
   352         }
       
   353     }
       
   354 
       
   355     if (m_image) {
       
   356         m_image = 0;
       
   357 
       
   358         QAbstractVideoSurface::stop();
       
   359     }
       
   360 
       
   361     return false;
       
   362 }
       
   363 
       
   364 void QX11VideoSurface::stop()
       
   365 {
       
   366     if (m_image) {
       
   367         XFree(m_image);
       
   368         m_image = 0;
       
   369 
       
   370         QAbstractVideoSurface::stop();
       
   371     }
       
   372 }
       
   373 
       
   374 bool QX11VideoSurface::present(const QVideoFrame &frame)
       
   375 {
       
   376     if (!m_image) {
       
   377         setError(StoppedError);
       
   378         return false;
       
   379     } else if (m_image->width != frame.width() || m_image->height != frame.height()) {
       
   380         setError(IncorrectFormatError);
       
   381         return false;
       
   382     } else {
       
   383         QVideoFrame frameCopy(frame);
       
   384 
       
   385         if (!frameCopy.map(QAbstractVideoBuffer::ReadOnly)) {
       
   386             setError(IncorrectFormatError);
       
   387             return false;
       
   388         } else {
       
   389             bool presented = false;
       
   390 
       
   391             if (frame.handleType() != QAbstractVideoBuffer::XvShmImageHandle &&
       
   392                 m_image->data_size > frame.mappedBytes()) {
       
   393                 qWarning("Insufficient frame buffer size");
       
   394                 setError(IncorrectFormatError);
       
   395             } else if (frame.handleType() != QAbstractVideoBuffer::XvShmImageHandle &&
       
   396                        m_image->num_planes > 0 &&
       
   397                        m_image->pitches[0] != frame.bytesPerLine()) {
       
   398                 qWarning("Incompatible frame pitches");
       
   399                 setError(IncorrectFormatError);
       
   400             } else {
       
   401                 if (frame.handleType() != QAbstractVideoBuffer::XvShmImageHandle) {
       
   402                     m_image->data = reinterpret_cast<char *>(frameCopy.bits());
       
   403 
       
   404                     //qDebug() << "copy frame";
       
   405                     XvPutImage(
       
   406                             QX11Info::display(),
       
   407                             m_portId,
       
   408                             m_winId,
       
   409                             m_gc,
       
   410                             m_image,
       
   411                             m_viewport.x(),
       
   412                             m_viewport.y(),
       
   413                             m_viewport.width(),
       
   414                             m_viewport.height(),
       
   415                             m_displayRect.x(),
       
   416                             m_displayRect.y(),
       
   417                             m_displayRect.width(),
       
   418                             m_displayRect.height());
       
   419 
       
   420                     m_image->data = 0;
       
   421                 } else {
       
   422                     XvImage *img = frame.handle().value<XvImage*>();
       
   423 
       
   424                     //qDebug() << "render directly";
       
   425                     if (img)
       
   426                         XvShmPutImage(
       
   427                            QX11Info::display(),
       
   428                            m_portId,
       
   429                            m_winId,
       
   430                            m_gc,
       
   431                            img,
       
   432                            m_viewport.x(),
       
   433                            m_viewport.y(),
       
   434                            m_viewport.width(),
       
   435                            m_viewport.height(),
       
   436                            m_displayRect.x(),
       
   437                            m_displayRect.y(),
       
   438                            m_displayRect.width(),
       
   439                            m_displayRect.height(),
       
   440                            false);
       
   441                 }
       
   442 
       
   443                 presented = true;
       
   444             }
       
   445 
       
   446             frameCopy.unmap();
       
   447 
       
   448             return presented;
       
   449         }
       
   450     }
       
   451 }
       
   452 
       
   453 bool QX11VideoSurface::findPort()
       
   454 {
       
   455     unsigned int count = 0;
       
   456     XvAdaptorInfo *adaptors = 0;
       
   457     bool portFound = false;
       
   458 
       
   459     if (XvQueryAdaptors(QX11Info::display(), m_winId, &count, &adaptors) == Success) {
       
   460 #ifdef Q_WS_MAEMO_5
       
   461             //the overlay xvideo adapter fails to switch winId,
       
   462             //prefer the "SGX Textured Video" adapter instead
       
   463         for (unsigned int i = count-1; i >=0 && !portFound; --i) {
       
   464 #else
       
   465         for (unsigned int i = 0; i < count && !portFound; ++i) {
       
   466 #endif
       
   467             if (adaptors[i].type & XvImageMask) {
       
   468                 m_portId = adaptors[i].base_id;
       
   469 
       
   470                 for (unsigned int j = 0; j < adaptors[i].num_ports && !portFound; ++j, ++m_portId)
       
   471                     portFound = XvGrabPort(QX11Info::display(), m_portId, 0) == Success;
       
   472             }
       
   473         }
       
   474         XvFreeAdaptorInfo(adaptors);
       
   475     }
       
   476 
       
   477     return portFound;
       
   478 }
       
   479 
       
   480 void QX11VideoSurface::querySupportedFormats()
       
   481 {
       
   482     int count = 0;
       
   483     if (XvImageFormatValues *imageFormats = XvListImageFormats(
       
   484             QX11Info::display(), m_portId, &count)) {
       
   485         const int rgbCount = sizeof(qt_xvRgbLookup) / sizeof(XvFormatRgb);
       
   486         const int yuvCount = sizeof(qt_xvYuvLookup) / sizeof(XvFormatYuv);
       
   487 
       
   488         for (int i = 0; i < count; ++i) {
       
   489             switch (imageFormats[i].type) {
       
   490             case XvRGB:
       
   491                 for (int j = 0; j < rgbCount; ++j) {
       
   492                     if (imageFormats[i] == qt_xvRgbLookup[j]) {
       
   493                         m_supportedPixelFormats.append(qt_xvRgbLookup[j].pixelFormat);
       
   494                         m_formatIds.append(imageFormats[i].id);
       
   495                         break;
       
   496                     }
       
   497                 }
       
   498                 break;
       
   499             case XvYUV:
       
   500                 for (int j = 0; j < yuvCount; ++j) {
       
   501                     if (imageFormats[i] == qt_xvYuvLookup[j]) {
       
   502                         m_supportedPixelFormats.append(qt_xvYuvLookup[j].pixelFormat);
       
   503                         m_formatIds.append(imageFormats[i].id);
       
   504                         break;
       
   505                     }
       
   506                 }
       
   507                 break;
       
   508             }
       
   509         }
       
   510         XFree(imageFormats);
       
   511     }
       
   512 
       
   513     m_brightnessRange = qMakePair(0, 0);
       
   514     m_contrastRange = qMakePair(0, 0);
       
   515     m_hueRange = qMakePair(0, 0);
       
   516     m_saturationRange = qMakePair(0, 0);
       
   517 
       
   518     if (XvAttribute *attributes = XvQueryPortAttributes(QX11Info::display(), m_portId, &count)) {
       
   519         for (int i = 0; i < count; ++i) {
       
   520             if (qstrcmp(attributes[i].name, "XV_BRIGHTNESS") == 0)
       
   521                 m_brightnessRange = qMakePair(attributes[i].min_value, attributes[i].max_value);
       
   522             else if (qstrcmp(attributes[i].name, "XV_CONTRAST") == 0)
       
   523                 m_contrastRange = qMakePair(attributes[i].min_value, attributes[i].max_value);
       
   524             else if (qstrcmp(attributes[i].name, "XV_HUE") == 0)
       
   525                 m_hueRange = qMakePair(attributes[i].min_value, attributes[i].max_value);
       
   526             else if (qstrcmp(attributes[i].name, "XV_SATURATION") == 0)
       
   527                 m_saturationRange = qMakePair(attributes[i].min_value, attributes[i].max_value);
       
   528         }
       
   529 
       
   530         XFree(attributes);
       
   531     }
       
   532 }
       
   533 
       
   534 #endif //QT_NO_XVIDEO
       
   535