QtGStreamer  0.10.2
 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 "../xoverlay.h"
21 #include "../pipeline.h"
22 #include "../bus.h"
23 #include "../message.h"
24 #include "../../QGlib/connect.h"
25 #include "../../QGlib/signal.h"
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 #include <QtGui/QApplication>
33 #include <QtGui/QHBoxLayout>
34 
35 #ifndef QTGSTREAMER_UI_NO_OPENGL
36 # include <QtOpenGL/QGLWidget>
37 #endif
38 
39 namespace QGst {
40 namespace Ui {
41 
42 class AbstractRenderer
43 {
44 public:
45  static AbstractRenderer *create(const ElementPtr & sink, QWidget *videoWidget);
46 
47  virtual ~AbstractRenderer() {}
48  virtual ElementPtr videoSink() const = 0;
49 };
50 
51 
52 class XOverlayRenderer : public QObject, public AbstractRenderer
53 {
54 public:
55  XOverlayRenderer(QWidget *parent)
56  : QObject(parent)
57  {
58  m_windowId = widget()->winId(); //create a new X window (if we are on X11 with alien widgets)
59  QApplication::syncX(); //inform other applications about the new window (on X11)
60 
61  widget()->installEventFilter(this);
62  widget()->setAttribute(Qt::WA_NoSystemBackground, true);
63  widget()->setAttribute(Qt::WA_PaintOnScreen, true);
64  widget()->update();
65  }
66 
67  virtual ~XOverlayRenderer()
68  {
69  if (m_sink) {
70  m_sink->setWindowHandle(0);
71  }
72  widget()->removeEventFilter(this);
73  widget()->setAttribute(Qt::WA_NoSystemBackground, false);
74  widget()->setAttribute(Qt::WA_PaintOnScreen, false);
75  widget()->update();
76  }
77 
78  void setVideoSink(const XOverlayPtr & sink)
79  {
80  QMutexLocker l(&m_sinkMutex);
81  if (m_sink) {
82  m_sink->setWindowHandle(0);
83  }
84  m_sink = sink;
85  if (m_sink) {
86  m_sink->setWindowHandle(m_windowId);
87  }
88  }
89 
90  virtual ElementPtr videoSink() const
91  {
92  QMutexLocker l(&m_sinkMutex);
93  return m_sink.dynamicCast<Element>();
94  }
95 
96 protected:
97  virtual bool eventFilter(QObject *filteredObject, QEvent *event)
98  {
99  if (filteredObject == parent() && event->type() == QEvent::Paint) {
100  QMutexLocker l(&m_sinkMutex);
101  State currentState = m_sink ? m_sink.dynamicCast<Element>()->currentState() : StateNull;
102 
103  if (currentState == StatePlaying || currentState == StatePaused) {
104  m_sink->expose();
105  } else {
106  QPainter p(widget());
107  p.fillRect(widget()->rect(), Qt::black);
108  }
109  return true;
110  } else {
111  return QObject::eventFilter(filteredObject, event);
112  }
113  }
114 
115 private:
116  inline QWidget *widget() { return static_cast<QWidget*>(parent()); }
117  WId m_windowId;
118  mutable QMutex m_sinkMutex;
119  XOverlayPtr m_sink;
120 };
121 
122 
123 class QtVideoSinkRenderer : public QObject, public AbstractRenderer
124 {
125 public:
126  QtVideoSinkRenderer(const ElementPtr & sink, QWidget *parent)
127  : QObject(parent), m_sink(sink)
128  {
129  QGlib::connect(sink, "update", this, &QtVideoSinkRenderer::onUpdate);
130  parent->installEventFilter(this);
131  parent->setAttribute(Qt::WA_OpaquePaintEvent, true);
132  }
133 
134  virtual ~QtVideoSinkRenderer()
135  {
136  widget()->removeEventFilter(this);
137  widget()->setAttribute(Qt::WA_OpaquePaintEvent, false);
138  }
139 
140  virtual ElementPtr videoSink() const { return m_sink; }
141 
142 protected:
143  virtual bool eventFilter(QObject *filteredObject, QEvent *event)
144  {
145  if (filteredObject == parent() && event->type() == QEvent::Paint) {
146  QPainter painter(widget());
147  QRect targetArea = widget()->rect();
148  QGlib::emit<void>(m_sink, "paint", (void*) &painter,
149  (qreal) targetArea.x(), (qreal) targetArea.y(),
150  (qreal) targetArea.width(), (qreal) targetArea.height());
151  return true;
152  } else {
153  return QObject::eventFilter(filteredObject, event);
154  }
155  }
156 
157 private:
158  inline QWidget *widget() { return static_cast<QWidget*>(parent()); }
159  void onUpdate() { widget()->update(); }
160 
161  ElementPtr m_sink;
162 };
163 
164 
165 #ifndef QTGSTREAMER_UI_NO_OPENGL
166 
167 class QtGLVideoSinkRenderer : public AbstractRenderer
168 {
169 public:
170  QtGLVideoSinkRenderer(const ElementPtr & sink, QWidget *parent)
171  {
172  m_layout = new QHBoxLayout(parent);
173  m_glWidget = new QGLWidget(parent);
174  m_layout->setContentsMargins(0, 0, 0, 0);
175  m_layout->addWidget(m_glWidget);
176  parent->setLayout(m_layout);
177 
178  m_renderer = new QtVideoSinkRenderer(sink, m_glWidget);
179 
180  m_glWidget->makeCurrent();
181  sink->setProperty("glcontext", (void*) QGLContext::currentContext());
182  m_glWidget->doneCurrent();
183  }
184 
185  virtual ~QtGLVideoSinkRenderer()
186  {
187  delete m_renderer;
188  delete m_glWidget;
189  delete m_layout;
190  }
191 
192  virtual ElementPtr videoSink() const { return m_renderer->videoSink(); }
193 
194 private:
195  QtVideoSinkRenderer *m_renderer;
196  QHBoxLayout *m_layout;
197  QGLWidget *m_glWidget;
198 };
199 
200 #endif // QTGSTREAMER_UI_NO_OPENGL
201 
202 
203 class QWidgetVideoSinkRenderer : public AbstractRenderer
204 {
205 public:
206  QWidgetVideoSinkRenderer(const ElementPtr & sink, QWidget *parent)
207  : m_sink(sink)
208  {
209  //GValue of G_TYPE_POINTER can only be set as void* in the bindings
210  m_sink->setProperty<void*>("widget", parent);
211  }
212 
213  virtual ~QWidgetVideoSinkRenderer()
214  {
215  m_sink->setProperty<void*>("widget", NULL);
216  }
217 
218  virtual ElementPtr videoSink() const { return m_sink; }
219 
220 private:
221  ElementPtr m_sink;
222 };
223 
224 
225 class PipelineWatch : public QObject, public AbstractRenderer
226 {
227 public:
228  PipelineWatch(const PipelinePtr & pipeline, QWidget *parent)
229  : QObject(parent), m_renderer(new XOverlayRenderer(parent)), m_pipeline(pipeline)
230  {
231  pipeline->bus()->enableSyncMessageEmission();
232  QGlib::connect(pipeline->bus(), "sync-message",
233  this, &PipelineWatch::onBusSyncMessage);
234  }
235 
236  virtual ~PipelineWatch()
237  {
238  m_pipeline->bus()->disableSyncMessageEmission();
239  delete m_renderer;
240  }
241 
242  virtual ElementPtr videoSink() const { return m_renderer->videoSink(); }
243 
244  void releaseSink() { m_renderer->setVideoSink(XOverlayPtr()); }
245 
246 private:
247  void onBusSyncMessage(const MessagePtr & msg)
248  {
249  switch (msg->type()) {
250  case MessageElement:
251  if (msg->internalStructure()->name() == QLatin1String("prepare-xwindow-id")) {
252  XOverlayPtr overlay = msg->source().dynamicCast<XOverlay>();
253  m_renderer->setVideoSink(overlay);
254  }
255  break;
256  case MessageStateChanged:
257  //release the sink when it goes back to null state
258  if (msg.staticCast<StateChangedMessage>()->newState() == StateNull &&
259  msg->source() == m_renderer->videoSink())
260  {
261  releaseSink();
262  }
263  default:
264  break;
265  }
266  }
267 
268 private:
269  XOverlayRenderer *m_renderer;
270  PipelinePtr m_pipeline;
271 };
272 
273 
274 AbstractRenderer *AbstractRenderer::create(const ElementPtr & sink, QWidget *videoWidget)
275 {
276  XOverlayPtr overlay = sink.dynamicCast<XOverlay>();
277  if (overlay) {
278  XOverlayRenderer *r = new XOverlayRenderer(videoWidget);
279  r->setVideoSink(overlay);
280  return r;
281  }
282 
283  if (QGlib::Type::fromInstance(sink).name() == QLatin1String("GstQtVideoSink")) {
284  return new QtVideoSinkRenderer(sink, videoWidget);
285  }
286 
287 #ifndef QTGSTREAMER_UI_NO_OPENGL
288  if (QGlib::Type::fromInstance(sink).name() == QLatin1String("GstQtGLVideoSink")) {
289  return new QtGLVideoSinkRenderer(sink, videoWidget);
290  }
291 #endif
292 
293  if (QGlib::Type::fromInstance(sink).name() == QLatin1String("GstQWidgetVideoSink")) {
294  return new QWidgetVideoSinkRenderer(sink, videoWidget);
295  }
296 
297  return NULL;
298 }
299 
300 
301 VideoWidget::VideoWidget(QWidget *parent, Qt::WindowFlags f)
302  : QWidget(parent, f), d(NULL)
303 {
304 }
305 
306 VideoWidget::~VideoWidget()
307 {
308  delete d;
309 }
310 
312 {
313  return d ? d->videoSink() : ElementPtr();
314 }
315 
317 {
318  if (!sink) {
320  return;
321  }
322 
323  Q_ASSERT(QThread::currentThread() == QApplication::instance()->thread());
324  Q_ASSERT(d == NULL);
325 
326  d = AbstractRenderer::create(sink, this);
327 
328  if (!d) {
329  qCritical() << "QGst::Ui::VideoWidget: Could not construct a renderer for the specified element";
330  }
331 }
332 
334 {
335  Q_ASSERT(QThread::currentThread() == QApplication::instance()->thread());
336 
337  if (d) {
338  PipelineWatch *pw = dynamic_cast<PipelineWatch*>(d);
339  if (pw) {
340  pw->releaseSink();
341  } else {
342  delete d;
343  d = NULL;
344  }
345  }
346 }
347 
349 {
350  if (!pipeline) {
352  return;
353  }
354 
355  Q_ASSERT(QThread::currentThread() == QApplication::instance()->thread());
356  Q_ASSERT(d == NULL);
357 
358  d = new PipelineWatch(pipeline, this);
359 }
360 
362 {
363  Q_ASSERT(QThread::currentThread() == QApplication::instance()->thread());
364 
365  if (dynamic_cast<PipelineWatch*>(d)) {
366  delete d;
367  d = NULL;
368  }
369 }
370 
371 void VideoWidget::paintEvent(QPaintEvent *event)
372 {
373  QPainter p(this);
374  p.fillRect(event->rect(), Qt::black);
375 }
376 
377 } //namespace Ui
378 } //namespace QGst