QtGStreamer 1.2.0
Loading...
Searching...
No Matches
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
45namespace QGst {
46namespace Ui {
47
48class AbstractRenderer
49{
50public:
51 static AbstractRenderer *create(const ElementPtr & sink, QWidget *videoWidget);
52
53 virtual ~AbstractRenderer() {}
54 virtual ElementPtr videoSink() const = 0;
55};
56
57
58class VideoOverlayRenderer : public QObject, public AbstractRenderer
59{
60public:
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
104protected:
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
123private:
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
131class QtVideoSinkRenderer : public QObject, public AbstractRenderer
132{
133public:
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
150protected:
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
165private:
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
175class QtGLVideoSinkRenderer : public AbstractRenderer
176{
177public:
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
202private:
203 QtVideoSinkRenderer *m_renderer;
204 QHBoxLayout *m_layout;
205 QGLWidget *m_glWidget;
206};
207
208#endif // QTGSTREAMER_UI_NO_OPENGL
209
210
211class QWidgetVideoSinkRenderer : public AbstractRenderer
212{
213public:
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
228private:
229 ElementPtr m_sink;
230};
231
232
233class PipelineWatch : public QObject, public AbstractRenderer
234{
235public:
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
254private:
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
276private:
277 VideoOverlayRenderer *m_renderer;
278 PipelinePtr m_pipeline;
279};
280
281
282AbstractRenderer *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
321VideoWidget::VideoWidget(QWidget *parent, Qt::WindowFlags f)
322 : QWidget(parent, f), d(NULL)
323{
324}
325
326VideoWidget::~VideoWidget()
327{
328 delete d;
329}
330
331ElementPtr VideoWidget::videoSink() const
332{
333 return d ? d->videoSink() : ElementPtr();
334}
335
336void VideoWidget::setVideoSink(const ElementPtr & sink)
337{
338 if (!sink) {
339 releaseVideoSink();
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
353void VideoWidget::releaseVideoSink()
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
368void VideoWidget::watchPipeline(const PipelinePtr & pipeline)
369{
370 if (!pipeline) {
371 stopPipelineWatch();
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
381void VideoWidget::stopPipelineWatch()
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
391void VideoWidget::paintEvent(QPaintEvent *event)
392{
393 QPainter p(this);
394 p.fillRect(event->rect(), Qt::black);
395}
396
397} //namespace Ui
398} //namespace QGst
RefPointer< X > dynamicCast() const
Definition refpointer.h:464
bool connect(void *instance, const char *detailedSignal, T *receiver, R(T::*slot)(Args...), ConnectFlags flags=0)
Wrappers for GStreamer classes.