QtGStreamer  1.2.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Pages
videowidget.cpp
1 /*
2  Copyright (C) 2010 George Kiagiadakis <kiagiadakis.george@gmail.com>
3  Copyright (C) 2011-2012 Collabora Ltd.
4  @author George Kiagiadakis <george.kiagiadakis@collabora.co.uk>
5 
6  This library is free software; you can redistribute it and/or modify
7  it under the terms of the GNU Lesser General Public License as published
8  by the Free Software Foundation; either version 2.1 of the License, or
9  (at your option) any later version.
10 
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU Lesser General Public License for more details.
15 
16  You should have received a copy of the GNU Lesser General Public License
17  along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19 #include "videowidget.h"
20 #include "../videooverlay.h"
21 #include "../pipeline.h"
22 #include "../bus.h"
23 #include "../message.h"
24 #include "../../QGlib/connect.h"
25 #include "../../QGlib/Signal"
26 #include <QtCore/QDebug>
27 #include <QtCore/QMutex>
28 #include <QtCore/QThread>
29 #include <QtGui/QPainter>
30 #include <QtGui/QPaintEvent>
31 #include <QtGui/QResizeEvent>
32 
33 #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
34 # include <QtWidgets/QApplication>
35 # include <QtWidgets/QHBoxLayout>
36 #else
37 # include <QtGui/QApplication>
38 # include <QtGui/QHBoxLayout>
39 #endif
40 
41 #ifndef QTGSTREAMER_UI_NO_OPENGL
42 # include <QtOpenGL/QGLWidget>
43 #endif
44 
45 namespace QGst {
46 namespace Ui {
47 
48 class AbstractRenderer
49 {
50 public:
51  static AbstractRenderer *create(const ElementPtr & sink, QWidget *videoWidget);
52 
53  virtual ~AbstractRenderer() {}
54  virtual ElementPtr videoSink() const = 0;
55 };
56 
57 
58 class VideoOverlayRenderer : public QObject, public AbstractRenderer
59 {
60 public:
61  VideoOverlayRenderer(QWidget *parent)
62  : QObject(parent)
63  {
64  m_windowId = widget()->winId(); //create a new X window (if we are on X11 with alien widgets)
65 #if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
66  QApplication::syncX(); //inform other applications about the new window (on X11)
67 #endif
68 
69  widget()->installEventFilter(this);
70  widget()->setAttribute(Qt::WA_NoSystemBackground, true);
71  widget()->setAttribute(Qt::WA_PaintOnScreen, true);
72  widget()->update();
73  }
74 
75  virtual ~VideoOverlayRenderer()
76  {
77  if (m_sink) {
78  m_sink->setWindowHandle(0);
79  }
80  widget()->removeEventFilter(this);
81  widget()->setAttribute(Qt::WA_NoSystemBackground, false);
82  widget()->setAttribute(Qt::WA_PaintOnScreen, false);
83  widget()->update();
84  }
85 
86  void setVideoSink(const VideoOverlayPtr & sink)
87  {
88  QMutexLocker l(&m_sinkMutex);
89  if (m_sink) {
90  m_sink->setWindowHandle(0);
91  }
92  m_sink = sink;
93  if (m_sink) {
94  m_sink->setWindowHandle(m_windowId);
95  }
96  }
97 
98  virtual ElementPtr videoSink() const
99  {
100  QMutexLocker l(&m_sinkMutex);
101  return m_sink.dynamicCast<Element>();
102  }
103 
104 protected:
105  virtual bool eventFilter(QObject *filteredObject, QEvent *event)
106  {
107  if (filteredObject == parent() && event->type() == QEvent::Paint) {
108  QMutexLocker l(&m_sinkMutex);
109  State currentState = m_sink ? m_sink.dynamicCast<Element>()->currentState() : StateNull;
110 
111  if (currentState == StatePlaying || currentState == StatePaused) {
112  m_sink->expose();
113  } else {
114  QPainter p(widget());
115  p.fillRect(widget()->rect(), Qt::black);
116  }
117  return true;
118  } else {
119  return QObject::eventFilter(filteredObject, event);
120  }
121  }
122 
123 private:
124  inline QWidget *widget() { return static_cast<QWidget*>(parent()); }
125  WId m_windowId;
126  mutable QMutex m_sinkMutex;
127  VideoOverlayPtr m_sink;
128 };
129 
130 
131 class QtVideoSinkRenderer : public QObject, public AbstractRenderer
132 {
133 public:
134  QtVideoSinkRenderer(const ElementPtr & sink, QWidget *parent)
135  : QObject(parent), m_sink(sink)
136  {
137  QGlib::connect(sink, "update", this, &QtVideoSinkRenderer::onUpdate);
138  parent->installEventFilter(this);
139  parent->setAttribute(Qt::WA_OpaquePaintEvent, true);
140  }
141 
142  virtual ~QtVideoSinkRenderer()
143  {
144  widget()->removeEventFilter(this);
145  widget()->setAttribute(Qt::WA_OpaquePaintEvent, false);
146  }
147 
148  virtual ElementPtr videoSink() const { return m_sink; }
149 
150 protected:
151  virtual bool eventFilter(QObject *filteredObject, QEvent *event)
152  {
153  if (filteredObject == parent() && event->type() == QEvent::Paint) {
154  QPainter painter(widget());
155  QRect targetArea = widget()->rect();
156  QGlib::emit<void>(m_sink, "paint", (void*) &painter,
157  (qreal) targetArea.x(), (qreal) targetArea.y(),
158  (qreal) targetArea.width(), (qreal) targetArea.height());
159  return true;
160  } else {
161  return QObject::eventFilter(filteredObject, event);
162  }
163  }
164 
165 private:
166  inline QWidget *widget() { return static_cast<QWidget*>(parent()); }
167  void onUpdate() { widget()->update(); }
168 
169  ElementPtr m_sink;
170 };
171 
172 
173 #ifndef QTGSTREAMER_UI_NO_OPENGL
174 
175 class QtGLVideoSinkRenderer : public AbstractRenderer
176 {
177 public:
178  QtGLVideoSinkRenderer(const ElementPtr & sink, QWidget *parent)
179  {
180  m_layout = new QHBoxLayout(parent);
181  m_glWidget = new QGLWidget(parent);
182  m_layout->setContentsMargins(0, 0, 0, 0);
183  m_layout->addWidget(m_glWidget);
184  parent->setLayout(m_layout);
185 
186  m_renderer = new QtVideoSinkRenderer(sink, m_glWidget);
187 
188  m_glWidget->makeCurrent();
189  sink->setProperty("glcontext", (void*) QGLContext::currentContext());
190  m_glWidget->doneCurrent();
191  }
192 
193  virtual ~QtGLVideoSinkRenderer()
194  {
195  delete m_renderer;
196  delete m_glWidget;
197  delete m_layout;
198  }
199 
200  virtual ElementPtr videoSink() const { return m_renderer->videoSink(); }
201 
202 private:
203  QtVideoSinkRenderer *m_renderer;
204  QHBoxLayout *m_layout;
205  QGLWidget *m_glWidget;
206 };
207 
208 #endif // QTGSTREAMER_UI_NO_OPENGL
209 
210 
211 class QWidgetVideoSinkRenderer : public AbstractRenderer
212 {
213 public:
214  QWidgetVideoSinkRenderer(const ElementPtr & sink, QWidget *parent)
215  : m_sink(sink)
216  {
217  //GValue of G_TYPE_POINTER can only be set as void* in the bindings
218  m_sink->setProperty<void*>("widget", parent);
219  }
220 
221  virtual ~QWidgetVideoSinkRenderer()
222  {
223  m_sink->setProperty<void*>("widget", NULL);
224  }
225 
226  virtual ElementPtr videoSink() const { return m_sink; }
227 
228 private:
229  ElementPtr m_sink;
230 };
231 
232 
233 class PipelineWatch : public QObject, public AbstractRenderer
234 {
235 public:
236  PipelineWatch(const PipelinePtr & pipeline, QWidget *parent)
237  : QObject(parent), m_renderer(new VideoOverlayRenderer(parent)), m_pipeline(pipeline)
238  {
239  pipeline->bus()->enableSyncMessageEmission();
240  QGlib::connect(pipeline->bus(), "sync-message",
241  this, &PipelineWatch::onBusSyncMessage);
242  }
243 
244  virtual ~PipelineWatch()
245  {
246  m_pipeline->bus()->disableSyncMessageEmission();
247  delete m_renderer;
248  }
249 
250  virtual ElementPtr videoSink() const { return m_renderer->videoSink(); }
251 
252  void releaseSink() { m_renderer->setVideoSink(VideoOverlayPtr()); }
253 
254 private:
255  void onBusSyncMessage(const MessagePtr & msg)
256  {
257  switch (msg->type()) {
258  case MessageElement:
259  if (VideoOverlay::isPrepareWindowHandleMessage(msg)) {
260  VideoOverlayPtr overlay = msg->source().dynamicCast<VideoOverlay>();
261  m_renderer->setVideoSink(overlay);
262  }
263  break;
264  case MessageStateChanged:
265  //release the sink when it goes back to null state
266  if (msg.staticCast<StateChangedMessage>()->newState() == StateNull &&
267  msg->source() == m_renderer->videoSink())
268  {
269  releaseSink();
270  }
271  default:
272  break;
273  }
274  }
275 
276 private:
277  VideoOverlayRenderer *m_renderer;
278  PipelinePtr m_pipeline;
279 };
280 
281 
282 AbstractRenderer *AbstractRenderer::create(const ElementPtr & sink, QWidget *videoWidget)
283 {
284  VideoOverlayPtr overlay = sink.dynamicCast<VideoOverlay>();
285  if (overlay) {
286  VideoOverlayRenderer *r = new VideoOverlayRenderer(videoWidget);
287  r->setVideoSink(overlay);
288  return r;
289  }
290 
291  if (QGlib::Type::fromInstance(sink).name() == QLatin1String("GstQtVideoSink"
292 #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
293  "_qt5"
294 #endif
295  )) {
296  return new QtVideoSinkRenderer(sink, videoWidget);
297  }
298 
299 #ifndef QTGSTREAMER_UI_NO_OPENGL
300  if (QGlib::Type::fromInstance(sink).name() == QLatin1String("GstQtGLVideoSink"
301 # if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
302  "_qt5"
303 # endif
304  )) {
305  return new QtGLVideoSinkRenderer(sink, videoWidget);
306  }
307 #endif
308 
309  if (QGlib::Type::fromInstance(sink).name() == QLatin1String("GstQWidgetVideoSink"
310 #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
311  "_qt5"
312 #endif
313  )) {
314  return new QWidgetVideoSinkRenderer(sink, videoWidget);
315  }
316 
317  return NULL;
318 }
319 
320 
321 VideoWidget::VideoWidget(QWidget *parent, Qt::WindowFlags f)
322  : QWidget(parent, f), d(NULL)
323 {
324 }
325 
326 VideoWidget::~VideoWidget()
327 {
328  delete d;
329 }
330 
332 {
333  return d ? d->videoSink() : ElementPtr();
334 }
335 
337 {
338  if (!sink) {
340  return;
341  }
342 
343  Q_ASSERT(QThread::currentThread() == QApplication::instance()->thread());
344  Q_ASSERT(d == NULL);
345 
346  d = AbstractRenderer::create(sink, this);
347 
348  if (!d) {
349  qCritical() << "QGst::Ui::VideoWidget: Could not construct a renderer for the specified element";
350  }
351 }
352 
354 {
355  Q_ASSERT(QThread::currentThread() == QApplication::instance()->thread());
356 
357  if (d) {
358  PipelineWatch *pw = dynamic_cast<PipelineWatch*>(d);
359  if (pw) {
360  pw->releaseSink();
361  } else {
362  delete d;
363  d = NULL;
364  }
365  }
366 }
367 
369 {
370  if (!pipeline) {
372  return;
373  }
374 
375  Q_ASSERT(QThread::currentThread() == QApplication::instance()->thread());
376  Q_ASSERT(d == NULL);
377 
378  d = new PipelineWatch(pipeline, this);
379 }
380 
382 {
383  Q_ASSERT(QThread::currentThread() == QApplication::instance()->thread());
384 
385  if (dynamic_cast<PipelineWatch*>(d)) {
386  delete d;
387  d = NULL;
388  }
389 }
390 
391 void VideoWidget::paintEvent(QPaintEvent *event)
392 {
393  QPainter p(this);
394  p.fillRect(event->rect(), Qt::black);
395 }
396 
397 } //namespace Ui
398 } //namespace QGst
ElementPtr videoSink() const
void watchPipeline(const PipelinePtr &pipeline)
bool connect(void *instance, const char *detailedSignal, T *receiver, R(T::*slot)(Args...), ConnectFlags flags=0)
Definition: connectimpl.h:186
void setVideoSink(const ElementPtr &sink)
RefPointer< X > dynamicCast() const
Definition: refpointer.h:462