videowidget.cpp

00001 /*
00002     Copyright (C) 2010 George Kiagiadakis <kiagiadakis.george@gmail.com>
00003     Copyright (C) 2011 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 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 <QtCore/QDebug>
00026 #include <QtCore/QMutex>
00027 #include <QtCore/QThread>
00028 #include <QtGui/QPainter>
00029 #include <QtGui/QPaintEvent>
00030 #include <QtGui/QResizeEvent>
00031 #include <QtGui/QApplication>
00032 
00033 namespace QGst {
00034 namespace Ui {
00035 
00036 class AbstractRenderer
00037 {
00038 public:
00039     static AbstractRenderer *create(const ElementPtr & sink, QWidget *videoWidget);
00040 
00041     virtual ~AbstractRenderer() {}
00042     virtual ElementPtr videoSink() const = 0;
00043 };
00044 
00045 
00046 class XOverlayRenderer : public QObject, public AbstractRenderer
00047 {
00048 public:
00049     XOverlayRenderer(QWidget *parent)
00050         : QObject(parent)
00051     {
00052         m_windowId = widget()->winId(); //create a new X window (if we are on X11 with alien widgets)
00053         QApplication::syncX(); //inform other applications about the new window (on X11)
00054 
00055         widget()->installEventFilter(this);
00056         widget()->setAttribute(Qt::WA_NoSystemBackground, true);
00057         widget()->setAttribute(Qt::WA_PaintOnScreen, true);
00058         widget()->update();
00059     }
00060 
00061     virtual ~XOverlayRenderer()
00062     {
00063         if (m_sink) {
00064             m_sink->setWindowHandle(0);
00065         }
00066         widget()->removeEventFilter(this);
00067         widget()->setAttribute(Qt::WA_NoSystemBackground, false);
00068         widget()->setAttribute(Qt::WA_PaintOnScreen, false);
00069         widget()->update();
00070     }
00071 
00072     void setVideoSink(const XOverlayPtr & sink)
00073     {
00074         QMutexLocker l(&m_sinkMutex);
00075         if (m_sink) {
00076             m_sink->setWindowHandle(0);
00077         }
00078         m_sink = sink;
00079         if (m_sink) {
00080             m_sink->setWindowHandle(m_windowId);
00081         }
00082     }
00083 
00084     virtual ElementPtr videoSink() const
00085     {
00086         QMutexLocker l(&m_sinkMutex);
00087         return m_sink.dynamicCast<Element>();
00088     }
00089 
00090 protected:
00091     virtual bool eventFilter(QObject *filteredObject, QEvent *event)
00092     {
00093         if (filteredObject == parent() && event->type() == QEvent::Paint) {
00094             QMutexLocker l(&m_sinkMutex);
00095             State currentState = m_sink ? m_sink.dynamicCast<Element>()->currentState() : StateNull;
00096 
00097             if (currentState == StatePlaying || currentState == StatePaused) {
00098                 m_sink->expose();
00099             } else {
00100                 QPainter p(widget());
00101                 p.fillRect(widget()->rect(), Qt::black);
00102             }
00103             return true;
00104         } else {
00105             return QObject::eventFilter(filteredObject, event);
00106         }
00107     }
00108 
00109 private:
00110     inline QWidget *widget() { return static_cast<QWidget*>(parent()); }
00111     WId m_windowId;
00112     mutable QMutex m_sinkMutex;
00113     XOverlayPtr m_sink;
00114 };
00115 
00116 
00117 class QWidgetVideoSinkRenderer : public AbstractRenderer
00118 {
00119 public:
00120     QWidgetVideoSinkRenderer(const ElementPtr & sink, QWidget *parent)
00121         : m_sink(sink)
00122     {
00123         //GValue of G_TYPE_POINTER can only be set as void* in the bindings
00124         m_sink->setProperty<void*>("widget", parent);
00125     }
00126 
00127     virtual ~QWidgetVideoSinkRenderer()
00128     {
00129         m_sink->setProperty<void*>("widget", NULL);
00130     }
00131 
00132     virtual ElementPtr videoSink() const { return m_sink; }
00133 
00134 private:
00135     ElementPtr m_sink;
00136 };
00137 
00138 
00139 class PipelineWatch : public QObject, public AbstractRenderer
00140 {
00141 public:
00142     PipelineWatch(const PipelinePtr & pipeline, QWidget *parent)
00143         : QObject(parent), m_renderer(new XOverlayRenderer(parent)), m_pipeline(pipeline)
00144     {
00145         pipeline->bus()->enableSyncMessageEmission();
00146         QGlib::connect(pipeline->bus(), "sync-message",
00147                        this, &PipelineWatch::onBusSyncMessage);
00148     }
00149 
00150     virtual ~PipelineWatch()
00151     {
00152         m_pipeline->bus()->disableSyncMessageEmission();
00153         delete m_renderer;
00154     }
00155 
00156     virtual ElementPtr videoSink() const { return m_renderer->videoSink(); }
00157 
00158     void releaseSink() { m_renderer->setVideoSink(XOverlayPtr()); }
00159 
00160 private:
00161     void onBusSyncMessage(const MessagePtr & msg)
00162     {
00163         switch (msg->type()) {
00164         case MessageElement:
00165             if (msg->internalStructure()->name() == QLatin1String("prepare-xwindow-id")) {
00166                 XOverlayPtr overlay = msg->source().dynamicCast<XOverlay>();
00167                 m_renderer->setVideoSink(overlay);
00168             }
00169             break;
00170         case MessageStateChanged:
00171             //release the sink when it goes back to null state
00172             if (msg.staticCast<StateChangedMessage>()->newState() == StateNull &&
00173                 msg->source() == m_renderer->videoSink())
00174             {
00175                 releaseSink();
00176             }
00177         default:
00178             break;
00179         }
00180     }
00181 
00182 private:
00183     XOverlayRenderer *m_renderer;
00184     PipelinePtr m_pipeline;
00185 };
00186 
00187 
00188 AbstractRenderer *AbstractRenderer::create(const ElementPtr & sink, QWidget *videoWidget)
00189 {
00190     XOverlayPtr overlay = sink.dynamicCast<XOverlay>();
00191     if (overlay) {
00192         XOverlayRenderer *r = new XOverlayRenderer(videoWidget);
00193         r->setVideoSink(overlay);
00194         return r;
00195     }
00196 
00197     if (QGlib::Type::fromInstance(sink).name() == QLatin1String("GstQWidgetVideoSink")) {
00198         return new QWidgetVideoSinkRenderer(sink, videoWidget);
00199     }
00200 
00201     return NULL;
00202 }
00203 
00204 
00205 VideoWidget::VideoWidget(QWidget *parent, Qt::WindowFlags f)
00206     : QWidget(parent, f), d(NULL)
00207 {
00208 }
00209 
00210 VideoWidget::~VideoWidget()
00211 {
00212     delete d;
00213 }
00214 
00215 ElementPtr VideoWidget::videoSink() const
00216 {
00217     return d ? d->videoSink() : ElementPtr();
00218 }
00219 
00220 void VideoWidget::setVideoSink(const ElementPtr & sink)
00221 {
00222     if (!sink) {
00223         releaseVideoSink();
00224         return;
00225     }
00226 
00227     Q_ASSERT(QThread::currentThread() == QApplication::instance()->thread());
00228     Q_ASSERT(d == NULL);
00229 
00230     d = AbstractRenderer::create(sink, this);
00231 
00232     if (!d) {
00233         qCritical() << "QGst::Ui::VideoWidget: Could not construct a renderer for the specified element";
00234     }
00235 }
00236 
00237 void VideoWidget::releaseVideoSink()
00238 {
00239     Q_ASSERT(QThread::currentThread() == QApplication::instance()->thread());
00240 
00241     if (d) {
00242         PipelineWatch *pw = dynamic_cast<PipelineWatch*>(d);
00243         if (pw) {
00244             pw->releaseSink();
00245         } else {
00246             delete d;
00247             d = NULL;
00248         }
00249     }
00250 }
00251 
00252 void VideoWidget::watchPipeline(const PipelinePtr & pipeline)
00253 {
00254     if (!pipeline) {
00255         stopPipelineWatch();
00256         return;
00257     }
00258 
00259     Q_ASSERT(QThread::currentThread() == QApplication::instance()->thread());
00260     Q_ASSERT(d == NULL);
00261 
00262     d = new PipelineWatch(pipeline, this);
00263 }
00264 
00265 void VideoWidget::stopPipelineWatch()
00266 {
00267     Q_ASSERT(QThread::currentThread() == QApplication::instance()->thread());
00268 
00269     if (dynamic_cast<PipelineWatch*>(d)) {
00270         delete d;
00271         d = NULL;
00272     }
00273 }
00274 
00275 void VideoWidget::paintEvent(QPaintEvent *event)
00276 {
00277     QPainter p(this);
00278     p.fillRect(event->rect(), Qt::black);
00279 }
00280 
00281 } //namespace Ui
00282 } //namespace QGst
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator