00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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();
00059 QApplication::syncX();
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
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
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 }
00378 }