QtGStreamer  0.10.2
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator
videowidget.cpp
00001 /*
00002     Copyright (C) 2010 George Kiagiadakis <kiagiadakis.george@gmail.com>
00003     Copyright (C) 2011-2012 Collabora Ltd.
00004       @author George Kiagiadakis <george.kiagiadakis@collabora.co.uk>
00005 
00006     This library is free software; you can redistribute it and/or modify
00007     it under the terms of the GNU Lesser General Public License as published
00008     by the Free Software Foundation; either version 2.1 of the License, or
00009     (at your option) any later version.
00010 
00011     This program is distributed in the hope that it will be useful,
00012     but WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014     GNU Lesser General Public License for more details.
00015 
00016     You should have received a copy of the GNU Lesser General Public License
00017     along with this program.  If not, see <http://www.gnu.org/licenses/>.
00018 */
00019 #include "videowidget.h"
00020 #include "../xoverlay.h"
00021 #include "../pipeline.h"
00022 #include "../bus.h"
00023 #include "../message.h"
00024 #include "../../QGlib/connect.h"
00025 #include "../../QGlib/signal.h"
00026 #include <QtCore/QDebug>
00027 #include <QtCore/QMutex>
00028 #include <QtCore/QThread>
00029 #include <QtGui/QPainter>
00030 #include <QtGui/QPaintEvent>
00031 #include <QtGui/QResizeEvent>
00032 #include <QtGui/QApplication>
00033 #include <QtGui/QHBoxLayout>
00034 
00035 #ifndef QTGSTREAMER_UI_NO_OPENGL
00036 # include <QtOpenGL/QGLWidget>
00037 #endif
00038 
00039 namespace QGst {
00040 namespace Ui {
00041 
00042 class AbstractRenderer
00043 {
00044 public:
00045     static AbstractRenderer *create(const ElementPtr & sink, QWidget *videoWidget);
00046 
00047     virtual ~AbstractRenderer() {}
00048     virtual ElementPtr videoSink() const = 0;
00049 };
00050 
00051 
00052 class XOverlayRenderer : public QObject, public AbstractRenderer
00053 {
00054 public:
00055     XOverlayRenderer(QWidget *parent)
00056         : QObject(parent)
00057     {
00058         m_windowId = widget()->winId(); //create a new X window (if we are on X11 with alien widgets)
00059         QApplication::syncX(); //inform other applications about the new window (on X11)
00060 
00061         widget()->installEventFilter(this);
00062         widget()->setAttribute(Qt::WA_NoSystemBackground, true);
00063         widget()->setAttribute(Qt::WA_PaintOnScreen, true);
00064         widget()->update();
00065     }
00066 
00067     virtual ~XOverlayRenderer()
00068     {
00069         if (m_sink) {
00070             m_sink->setWindowHandle(0);
00071         }
00072         widget()->removeEventFilter(this);
00073         widget()->setAttribute(Qt::WA_NoSystemBackground, false);
00074         widget()->setAttribute(Qt::WA_PaintOnScreen, false);
00075         widget()->update();
00076     }
00077 
00078     void setVideoSink(const XOverlayPtr & sink)
00079     {
00080         QMutexLocker l(&m_sinkMutex);
00081         if (m_sink) {
00082             m_sink->setWindowHandle(0);
00083         }
00084         m_sink = sink;
00085         if (m_sink) {
00086             m_sink->setWindowHandle(m_windowId);
00087         }
00088     }
00089 
00090     virtual ElementPtr videoSink() const
00091     {
00092         QMutexLocker l(&m_sinkMutex);
00093         return m_sink.dynamicCast<Element>();
00094     }
00095 
00096 protected:
00097     virtual bool eventFilter(QObject *filteredObject, QEvent *event)
00098     {
00099         if (filteredObject == parent() && event->type() == QEvent::Paint) {
00100             QMutexLocker l(&m_sinkMutex);
00101             State currentState = m_sink ? m_sink.dynamicCast<Element>()->currentState() : StateNull;
00102 
00103             if (currentState == StatePlaying || currentState == StatePaused) {
00104                 m_sink->expose();
00105             } else {
00106                 QPainter p(widget());
00107                 p.fillRect(widget()->rect(), Qt::black);
00108             }
00109             return true;
00110         } else {
00111             return QObject::eventFilter(filteredObject, event);
00112         }
00113     }
00114 
00115 private:
00116     inline QWidget *widget() { return static_cast<QWidget*>(parent()); }
00117     WId m_windowId;
00118     mutable QMutex m_sinkMutex;
00119     XOverlayPtr m_sink;
00120 };
00121 
00122 
00123 class QtVideoSinkRenderer : public QObject, public AbstractRenderer
00124 {
00125 public:
00126     QtVideoSinkRenderer(const ElementPtr & sink, QWidget *parent)
00127         : QObject(parent), m_sink(sink)
00128     {
00129         QGlib::connect(sink, "update", this, &QtVideoSinkRenderer::onUpdate);
00130         parent->installEventFilter(this);
00131         parent->setAttribute(Qt::WA_OpaquePaintEvent, true);
00132     }
00133 
00134     virtual ~QtVideoSinkRenderer()
00135     {
00136         widget()->removeEventFilter(this);
00137         widget()->setAttribute(Qt::WA_OpaquePaintEvent, false);
00138     }
00139 
00140     virtual ElementPtr videoSink() const { return m_sink; }
00141 
00142 protected:
00143     virtual bool eventFilter(QObject *filteredObject, QEvent *event)
00144     {
00145         if (filteredObject == parent() && event->type() == QEvent::Paint) {
00146             QPainter painter(widget());
00147             QRect targetArea = widget()->rect();
00148             QGlib::emit<void>(m_sink, "paint", (void*) &painter,
00149                               (qreal) targetArea.x(), (qreal) targetArea.y(),
00150                               (qreal) targetArea.width(), (qreal) targetArea.height());
00151             return true;
00152         } else {
00153             return QObject::eventFilter(filteredObject, event);
00154         }
00155     }
00156 
00157 private:
00158     inline QWidget *widget() { return static_cast<QWidget*>(parent()); }
00159     void onUpdate() { widget()->update(); }
00160 
00161     ElementPtr m_sink;
00162 };
00163 
00164 
00165 #ifndef QTGSTREAMER_UI_NO_OPENGL
00166 
00167 class QtGLVideoSinkRenderer : public AbstractRenderer
00168 {
00169 public:
00170     QtGLVideoSinkRenderer(const ElementPtr & sink, QWidget *parent)
00171     {
00172         m_layout = new QHBoxLayout(parent);
00173         m_glWidget = new QGLWidget(parent);
00174         m_layout->setContentsMargins(0, 0, 0, 0);
00175         m_layout->addWidget(m_glWidget);
00176         parent->setLayout(m_layout);
00177 
00178         m_renderer = new QtVideoSinkRenderer(sink, m_glWidget);
00179 
00180         m_glWidget->makeCurrent();
00181         sink->setProperty("glcontext", (void*) QGLContext::currentContext());
00182         m_glWidget->doneCurrent();
00183     }
00184 
00185     virtual ~QtGLVideoSinkRenderer()
00186     {
00187         delete m_renderer;
00188         delete m_glWidget;
00189         delete m_layout;
00190     }
00191 
00192     virtual ElementPtr videoSink() const { return m_renderer->videoSink(); }
00193 
00194 private:
00195     QtVideoSinkRenderer *m_renderer;
00196     QHBoxLayout *m_layout;
00197     QGLWidget *m_glWidget;
00198 };
00199 
00200 #endif // QTGSTREAMER_UI_NO_OPENGL
00201 
00202 
00203 class QWidgetVideoSinkRenderer : public AbstractRenderer
00204 {
00205 public:
00206     QWidgetVideoSinkRenderer(const ElementPtr & sink, QWidget *parent)
00207         : m_sink(sink)
00208     {
00209         //GValue of G_TYPE_POINTER can only be set as void* in the bindings
00210         m_sink->setProperty<void*>("widget", parent);
00211     }
00212 
00213     virtual ~QWidgetVideoSinkRenderer()
00214     {
00215         m_sink->setProperty<void*>("widget", NULL);
00216     }
00217 
00218     virtual ElementPtr videoSink() const { return m_sink; }
00219 
00220 private:
00221     ElementPtr m_sink;
00222 };
00223 
00224 
00225 class PipelineWatch : public QObject, public AbstractRenderer
00226 {
00227 public:
00228     PipelineWatch(const PipelinePtr & pipeline, QWidget *parent)
00229         : QObject(parent), m_renderer(new XOverlayRenderer(parent)), m_pipeline(pipeline)
00230     {
00231         pipeline->bus()->enableSyncMessageEmission();
00232         QGlib::connect(pipeline->bus(), "sync-message",
00233                        this, &PipelineWatch::onBusSyncMessage);
00234     }
00235 
00236     virtual ~PipelineWatch()
00237     {
00238         m_pipeline->bus()->disableSyncMessageEmission();
00239         delete m_renderer;
00240     }
00241 
00242     virtual ElementPtr videoSink() const { return m_renderer->videoSink(); }
00243 
00244     void releaseSink() { m_renderer->setVideoSink(XOverlayPtr()); }
00245 
00246 private:
00247     void onBusSyncMessage(const MessagePtr & msg)
00248     {
00249         switch (msg->type()) {
00250         case MessageElement:
00251             if (msg->internalStructure()->name() == QLatin1String("prepare-xwindow-id")) {
00252                 XOverlayPtr overlay = msg->source().dynamicCast<XOverlay>();
00253                 m_renderer->setVideoSink(overlay);
00254             }
00255             break;
00256         case MessageStateChanged:
00257             //release the sink when it goes back to null state
00258             if (msg.staticCast<StateChangedMessage>()->newState() == StateNull &&
00259                 msg->source() == m_renderer->videoSink())
00260             {
00261                 releaseSink();
00262             }
00263         default:
00264             break;
00265         }
00266     }
00267 
00268 private:
00269     XOverlayRenderer *m_renderer;
00270     PipelinePtr m_pipeline;
00271 };
00272 
00273 
00274 AbstractRenderer *AbstractRenderer::create(const ElementPtr & sink, QWidget *videoWidget)
00275 {
00276     XOverlayPtr overlay = sink.dynamicCast<XOverlay>();
00277     if (overlay) {
00278         XOverlayRenderer *r = new XOverlayRenderer(videoWidget);
00279         r->setVideoSink(overlay);
00280         return r;
00281     }
00282 
00283     if (QGlib::Type::fromInstance(sink).name() == QLatin1String("GstQtVideoSink")) {
00284         return new QtVideoSinkRenderer(sink, videoWidget);
00285     }
00286 
00287 #ifndef QTGSTREAMER_UI_NO_OPENGL
00288     if (QGlib::Type::fromInstance(sink).name() == QLatin1String("GstQtGLVideoSink")) {
00289         return new QtGLVideoSinkRenderer(sink, videoWidget);
00290     }
00291 #endif
00292 
00293     if (QGlib::Type::fromInstance(sink).name() == QLatin1String("GstQWidgetVideoSink")) {
00294         return new QWidgetVideoSinkRenderer(sink, videoWidget);
00295     }
00296 
00297     return NULL;
00298 }
00299 
00300 
00301 VideoWidget::VideoWidget(QWidget *parent, Qt::WindowFlags f)
00302     : QWidget(parent, f), d(NULL)
00303 {
00304 }
00305 
00306 VideoWidget::~VideoWidget()
00307 {
00308     delete d;
00309 }
00310 
00311 ElementPtr VideoWidget::videoSink() const
00312 {
00313     return d ? d->videoSink() : ElementPtr();
00314 }
00315 
00316 void VideoWidget::setVideoSink(const ElementPtr & sink)
00317 {
00318     if (!sink) {
00319         releaseVideoSink();
00320         return;
00321     }
00322 
00323     Q_ASSERT(QThread::currentThread() == QApplication::instance()->thread());
00324     Q_ASSERT(d == NULL);
00325 
00326     d = AbstractRenderer::create(sink, this);
00327 
00328     if (!d) {
00329         qCritical() << "QGst::Ui::VideoWidget: Could not construct a renderer for the specified element";
00330     }
00331 }
00332 
00333 void VideoWidget::releaseVideoSink()
00334 {
00335     Q_ASSERT(QThread::currentThread() == QApplication::instance()->thread());
00336 
00337     if (d) {
00338         PipelineWatch *pw = dynamic_cast<PipelineWatch*>(d);
00339         if (pw) {
00340             pw->releaseSink();
00341         } else {
00342             delete d;
00343             d = NULL;
00344         }
00345     }
00346 }
00347 
00348 void VideoWidget::watchPipeline(const PipelinePtr & pipeline)
00349 {
00350     if (!pipeline) {
00351         stopPipelineWatch();
00352         return;
00353     }
00354 
00355     Q_ASSERT(QThread::currentThread() == QApplication::instance()->thread());
00356     Q_ASSERT(d == NULL);
00357 
00358     d = new PipelineWatch(pipeline, this);
00359 }
00360 
00361 void VideoWidget::stopPipelineWatch()
00362 {
00363     Q_ASSERT(QThread::currentThread() == QApplication::instance()->thread());
00364 
00365     if (dynamic_cast<PipelineWatch*>(d)) {
00366         delete d;
00367         d = NULL;
00368     }
00369 }
00370 
00371 void VideoWidget::paintEvent(QPaintEvent *event)
00372 {
00373     QPainter p(this);
00374     p.fillRect(event->rect(), Qt::black);
00375 }
00376 
00377 } //namespace Ui
00378 } //namespace QGst