src/3rdparty/phonon/gstreamer/videowidget.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /*  This file is part of the KDE project.
       
     2 
       
     3     Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 
       
     5     This library is free software: you can redistribute it and/or modify
       
     6     it under the terms of the GNU Lesser General Public License as published by
       
     7     the Free Software Foundation, either version 2.1 or 3 of the License.
       
     8 
       
     9     This library is distributed in the hope that it will be useful,
       
    10     but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    12     GNU Lesser General Public License for more details.
       
    13 
       
    14     You should have received a copy of the GNU Lesser General Public License
       
    15     along with this library.  If not, see <http://www.gnu.org/licenses/>.
       
    16 */
       
    17 
       
    18 #include "videowidget.h"
       
    19 #include <QtCore/QEvent>
       
    20 #include <QtGui/QResizeEvent>
       
    21 #include <QtGui/QPalette>
       
    22 #include <QtGui/QImage>
       
    23 #include <QtGui/QPainter>
       
    24 #include <QtGui/QBoxLayout>
       
    25 #include <QApplication>
       
    26 #include <gst/gst.h>
       
    27 #include <gst/interfaces/propertyprobe.h>
       
    28 #include "mediaobject.h"
       
    29 #include "message.h"
       
    30 #include "common.h"
       
    31 
       
    32 #include "glrenderer.h"
       
    33 #include "widgetrenderer.h"
       
    34 #include "x11renderer.h"
       
    35 
       
    36 QT_BEGIN_NAMESPACE
       
    37 
       
    38 namespace Phonon
       
    39 {
       
    40 namespace Gstreamer
       
    41 {
       
    42 
       
    43 VideoWidget::VideoWidget(Backend *backend, QWidget *parent) :
       
    44     QWidget(parent),
       
    45     MediaNode(backend, VideoSink),
       
    46     m_videoBin(0),
       
    47     m_renderer(0),
       
    48     m_aspectRatio(Phonon::VideoWidget::AspectRatioAuto),
       
    49     m_brightness(0.0),
       
    50     m_hue(0.0),
       
    51     m_contrast(0.0),
       
    52     m_saturation(0.0),
       
    53     m_scaleMode(Phonon::VideoWidget::FitInView),
       
    54     m_videoBalance(0),
       
    55     m_colorspace(0),
       
    56     m_videoplug(0)
       
    57 {
       
    58     setupVideoBin();
       
    59 }
       
    60 
       
    61 VideoWidget::~VideoWidget()
       
    62 {
       
    63     if (m_videoBin) {
       
    64         gst_element_set_state (m_videoBin, GST_STATE_NULL);
       
    65         gst_object_unref (m_videoBin);
       
    66     }
       
    67 
       
    68     if (m_renderer)
       
    69         delete m_renderer;
       
    70 }
       
    71 
       
    72 
       
    73 void VideoWidget::setupVideoBin()
       
    74 {
       
    75 
       
    76     m_renderer = m_backend->deviceManager()->createVideoRenderer(this);
       
    77     GstElement *videoSink = m_renderer->videoSink();
       
    78 
       
    79     m_videoBin = gst_bin_new (NULL);
       
    80     Q_ASSERT(m_videoBin);
       
    81     gst_object_ref (GST_OBJECT (m_videoBin)); //Take ownership
       
    82     gst_object_sink (GST_OBJECT (m_videoBin));
       
    83 
       
    84     //The videoplug element is the final element before the pluggable videosink
       
    85     m_videoplug = gst_element_factory_make ("identity", NULL);
       
    86 
       
    87     //Colorspace ensures that the output of the stream matches the input format accepted by our video sink
       
    88     m_colorspace = gst_element_factory_make ("ffmpegcolorspace", NULL);
       
    89 
       
    90     //Video scale is used to prepare the correct aspect ratio and scale.
       
    91     GstElement *videoScale = gst_element_factory_make ("videoscale", NULL);
       
    92 
       
    93     //We need a queue to support the tee from parent node
       
    94     GstElement *queue = gst_element_factory_make ("queue", NULL);
       
    95 
       
    96     if (queue && m_videoBin && videoScale && m_colorspace && videoSink && m_videoplug) {
       
    97         //Ensure that the bare essentials are prepared
       
    98         gst_bin_add_many (GST_BIN (m_videoBin), queue, m_colorspace, m_videoplug, videoScale, videoSink, (const char*)NULL);
       
    99         bool success = false;
       
   100         //Video balance controls color/sat/hue in the YUV colorspace
       
   101         m_videoBalance = gst_element_factory_make ("videobalance", NULL);
       
   102         if (m_videoBalance) {
       
   103             // For video balance to work we have to first ensure that the video is in YUV colorspace,
       
   104             // then hand it off to the videobalance filter before finally converting it back to RGB.
       
   105             // Hence we nede a videoFilter to convert the colorspace before and after videobalance
       
   106             GstElement *m_colorspace2 = gst_element_factory_make ("ffmpegcolorspace", NULL);
       
   107             gst_bin_add_many(GST_BIN(m_videoBin), m_videoBalance, m_colorspace2, (const char*)NULL);
       
   108             success = gst_element_link_many(queue, m_colorspace, m_videoBalance, m_colorspace2, videoScale, m_videoplug, videoSink, (const char*)NULL);
       
   109         } else {
       
   110             //If video balance is not available, just connect to sink directly
       
   111             success = gst_element_link_many(queue, m_colorspace, videoScale, m_videoplug, videoSink, (const char*)NULL);
       
   112         }
       
   113 
       
   114         if (success) {
       
   115             GstPad *videopad = gst_element_get_pad (queue, "sink");
       
   116             gst_element_add_pad (m_videoBin, gst_ghost_pad_new ("sink", videopad));
       
   117             gst_object_unref (videopad);
       
   118             QWidget *parentWidget = qobject_cast<QWidget*>(parent());
       
   119             if (parentWidget)
       
   120                 parentWidget->winId();  // Due to some existing issues with alien in 4.4,
       
   121                                         //  we must currently force the creation of a parent widget.
       
   122             m_isValid = true; //initialization ok, accept input
       
   123         }
       
   124     }
       
   125 }
       
   126 
       
   127 void VideoWidget::paintEvent(QPaintEvent *event)
       
   128 {
       
   129     Q_ASSERT(m_renderer);
       
   130     m_renderer->handlePaint(event);
       
   131 }
       
   132 
       
   133 void VideoWidget::setVisible(bool val) {
       
   134     Q_ASSERT(m_renderer);
       
   135 
       
   136     // Disable overlays for graphics view
       
   137     if (root() && window() && window()->testAttribute(Qt::WA_DontShowOnScreen) && !m_renderer->paintsOnWidget()) {
       
   138         m_backend->logMessage(QString("Widget rendering forced"), Backend::Info, this);
       
   139         GstElement *videoSink = m_renderer->videoSink();
       
   140         Q_ASSERT(videoSink);
       
   141 
       
   142         gst_element_set_state (videoSink, GST_STATE_NULL);
       
   143         gst_bin_remove(GST_BIN(m_videoBin), videoSink);
       
   144         delete m_renderer;
       
   145         m_renderer = 0;
       
   146 
       
   147         // Use widgetRenderer as a fallback
       
   148         m_renderer = new WidgetRenderer(this);
       
   149         videoSink = m_renderer->videoSink();
       
   150         gst_bin_add(GST_BIN(m_videoBin), videoSink);
       
   151         gst_element_link(m_videoplug, videoSink);
       
   152         gst_element_set_state (videoSink, GST_STATE_PAUSED);
       
   153 
       
   154         // Request return to current state
       
   155         root()->invalidateGraph();
       
   156         root()->setState(root()->state());
       
   157     }
       
   158     QWidget::setVisible(val);    
       
   159 }
       
   160 
       
   161 bool VideoWidget::event(QEvent *event)
       
   162 {
       
   163     if (m_renderer && m_renderer->eventFilter(event))
       
   164         return true;
       
   165     return QWidget::event(event);
       
   166 }
       
   167 
       
   168 Phonon::VideoWidget::AspectRatio VideoWidget::aspectRatio() const
       
   169 {
       
   170     return m_aspectRatio;
       
   171 }
       
   172 
       
   173 QSize VideoWidget::sizeHint() const
       
   174 {
       
   175     if (!m_movieSize.isEmpty())
       
   176         return m_movieSize;
       
   177     else
       
   178         return QSize(640, 480);
       
   179 }
       
   180 
       
   181 void VideoWidget::setAspectRatio(Phonon::VideoWidget::AspectRatio aspectRatio)
       
   182 {
       
   183     m_aspectRatio = aspectRatio;
       
   184     if (m_renderer)
       
   185         m_renderer->aspectRatioChanged(aspectRatio);
       
   186 }
       
   187 
       
   188 Phonon::VideoWidget::ScaleMode VideoWidget::scaleMode() const
       
   189 {
       
   190     return m_scaleMode;
       
   191 }
       
   192 
       
   193 QRect VideoWidget::scaleToAspect(QRect srcRect, int w, int h) const
       
   194 {
       
   195     float width = srcRect.width();
       
   196     float height = srcRect.width() * (float(h) / float(w));
       
   197     if (height > srcRect.height()) {
       
   198         height = srcRect.height();
       
   199         width = srcRect.height() * (float(w) / float(h));
       
   200     }
       
   201     return QRect(0, 0, (int)width, (int)height);
       
   202 }
       
   203 
       
   204 /***
       
   205  * Calculates the actual rectangle the movie will be presented with
       
   206  **/
       
   207 QRect VideoWidget::calculateDrawFrameRect() const
       
   208 {
       
   209     QRect widgetRect = rect();
       
   210     QRect drawFrameRect;
       
   211     // Set m_drawFrameRect to be the size of the smallest possible
       
   212     // rect conforming to the aspect and containing the whole frame:
       
   213     switch (aspectRatio()) {
       
   214 
       
   215     case Phonon::VideoWidget::AspectRatioWidget:
       
   216         drawFrameRect = widgetRect;
       
   217         // No more calculations needed.
       
   218         return drawFrameRect;
       
   219 
       
   220     case Phonon::VideoWidget::AspectRatio4_3:
       
   221         drawFrameRect = scaleToAspect(widgetRect, 4, 3);
       
   222         break;
       
   223 
       
   224     case Phonon::VideoWidget::AspectRatio16_9:
       
   225         drawFrameRect = scaleToAspect(widgetRect, 16, 9);
       
   226         break;
       
   227 
       
   228     case Phonon::VideoWidget::AspectRatioAuto:
       
   229     default:
       
   230         drawFrameRect = QRect(0, 0, movieSize().width(), movieSize().height());
       
   231         break;
       
   232     }
       
   233 
       
   234     // Scale m_drawFrameRect to fill the widget
       
   235     // without breaking aspect:
       
   236     float widgetWidth = widgetRect.width();
       
   237     float widgetHeight = widgetRect.height();
       
   238     float frameWidth = widgetWidth;
       
   239     float frameHeight = drawFrameRect.height() * float(widgetWidth) / float(drawFrameRect.width());
       
   240 
       
   241     switch (scaleMode()) {
       
   242     case Phonon::VideoWidget::ScaleAndCrop:
       
   243         if (frameHeight < widgetHeight) {
       
   244             frameWidth *= float(widgetHeight) / float(frameHeight);
       
   245             frameHeight = widgetHeight;
       
   246         }
       
   247         break;
       
   248     case Phonon::VideoWidget::FitInView:
       
   249     default:
       
   250         if (frameHeight > widgetHeight) {
       
   251             frameWidth *= float(widgetHeight) / float(frameHeight);
       
   252             frameHeight = widgetHeight;
       
   253         }
       
   254         break;
       
   255     }
       
   256     drawFrameRect.setSize(QSize(int(frameWidth), int(frameHeight)));
       
   257     drawFrameRect.moveTo(int((widgetWidth - frameWidth) / 2.0f),
       
   258                            int((widgetHeight - frameHeight) / 2.0f));
       
   259     return drawFrameRect;
       
   260 }
       
   261 
       
   262 void VideoWidget::setScaleMode(Phonon::VideoWidget::ScaleMode scaleMode)
       
   263 {
       
   264     m_scaleMode = scaleMode;
       
   265     if (m_renderer)
       
   266         m_renderer->scaleModeChanged(scaleMode);
       
   267 }
       
   268 
       
   269 qreal VideoWidget::brightness() const
       
   270 {
       
   271     return m_brightness;
       
   272 }
       
   273 
       
   274 qreal clampedValue(qreal val)
       
   275 {
       
   276     if (val > 1.0 )
       
   277         return 1.0;
       
   278     else if (val < -1.0)
       
   279         return -1.0;
       
   280     else return val;
       
   281 }
       
   282 
       
   283 void VideoWidget::setBrightness(qreal newValue)
       
   284 {
       
   285     newValue = clampedValue(newValue);
       
   286 
       
   287     if (newValue == m_brightness)
       
   288         return;
       
   289 
       
   290     m_brightness = newValue;
       
   291 
       
   292     if (m_videoBalance)
       
   293         g_object_set(G_OBJECT(m_videoBalance), "brightness", newValue, (const char*)NULL); //gstreamer range is [-1, 1]
       
   294 
       
   295 }
       
   296 
       
   297 qreal VideoWidget::contrast() const
       
   298 {
       
   299     return m_contrast;
       
   300 }
       
   301 
       
   302 void VideoWidget::setContrast(qreal newValue)
       
   303 {
       
   304     newValue = clampedValue(newValue);
       
   305 
       
   306     if (newValue == m_contrast)
       
   307         return;
       
   308 
       
   309     m_contrast = newValue;
       
   310 
       
   311     if (m_videoBalance)
       
   312         g_object_set(G_OBJECT(m_videoBalance), "contrast", (newValue + 1.0), (const char*)NULL); //gstreamer range is [0-2]
       
   313 }
       
   314 
       
   315 qreal VideoWidget::hue() const
       
   316 {
       
   317     return m_hue;
       
   318 }
       
   319 
       
   320 void VideoWidget::setHue(qreal newValue)
       
   321 {
       
   322     if (newValue == m_hue)
       
   323         return;
       
   324 
       
   325     newValue = clampedValue(newValue);
       
   326 
       
   327     m_hue = newValue;
       
   328 
       
   329     if (m_videoBalance)
       
   330         g_object_set(G_OBJECT(m_videoBalance), "hue", newValue, (const char*)NULL); //gstreamer range is [-1, 1]
       
   331 }
       
   332 
       
   333 qreal VideoWidget::saturation() const
       
   334 {
       
   335     return m_saturation;
       
   336 }
       
   337 
       
   338 void VideoWidget::setSaturation(qreal newValue)
       
   339 {
       
   340     newValue = clampedValue(newValue);
       
   341 
       
   342     if (newValue == m_saturation)
       
   343         return;
       
   344 
       
   345     m_saturation = newValue;
       
   346 
       
   347     if (m_videoBalance)
       
   348         g_object_set(G_OBJECT(m_videoBalance), "saturation", newValue + 1.0, (const char*)NULL); //gstreamer range is [0, 2]
       
   349 }
       
   350 
       
   351 
       
   352 void VideoWidget::setMovieSize(const QSize &size)
       
   353 {
       
   354     m_backend->logMessage(QString("New video size %0 x %1").arg(size.width()).arg(size.height()), Backend::Info);
       
   355     if (size == m_movieSize)
       
   356         return;
       
   357     m_movieSize = size;
       
   358     widget()->updateGeometry();
       
   359     widget()->update();
       
   360 
       
   361     if (m_renderer)
       
   362         m_renderer->movieSizeChanged(m_movieSize);
       
   363 }
       
   364 
       
   365 void VideoWidget::mediaNodeEvent(const MediaNodeEvent *event)
       
   366 {
       
   367     switch (event->type()) {
       
   368     case MediaNodeEvent::VideoSizeChanged: {
       
   369             const QSize *size = static_cast<const QSize*>(event->data());
       
   370             setMovieSize(*size);
       
   371         }
       
   372         break;
       
   373     default:
       
   374         break;
       
   375     }
       
   376 
       
   377     // Forward events to renderer
       
   378     if (m_renderer)
       
   379         m_renderer->handleMediaNodeEvent(event);
       
   380 }
       
   381 
       
   382 }
       
   383 } //namespace Phonon::Gstreamer
       
   384 
       
   385 QT_END_NAMESPACE
       
   386 
       
   387 #include "moc_videowidget.cpp"