More fixes for Qt5
[harmattan/cameraplus] / declarative / videoplayer.cpp
1 /*!
2  * This file is part of CameraPlus.
3  *
4  * Copyright (C) 2012-2013 Mohammed Sameer <msameer@foolab.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20
21 #include "videoplayer.h"
22 #if defined(QT4)
23 #include <QDeclarativeInfo>
24 #elif defined(QT5)
25 #include <QQmlInfo>
26 #endif
27 #include "cameraconfig.h"
28 #include <QTimer>
29 #include "qtcamviewfinderrenderer.h"
30 #include <QPainter>
31
32 #if defined(QT4)
33 VideoPlayer::VideoPlayer(QDeclarativeItem *parent) :
34   QDeclarativeItem(parent),
35 #elif defined(QT5)
36 VideoPlayer::VideoPlayer(QQuickItem *parent) :
37   QQuickPaintedItem(parent),
38 #endif
39   m_config(0),
40   m_renderer(0),
41   m_bin(0),
42   m_state(VideoPlayer::StateStopped),
43   m_timer(new QTimer(this)),
44   m_pos(0) {
45
46   m_timer->setSingleShot(false);
47   m_timer->setInterval(500);
48   QObject::connect(m_timer, SIGNAL(timeout()), this, SIGNAL(positionChanged()));
49
50 #if defined(QT4)
51   setFlag(QGraphicsItem::ItemHasNoContents, false);
52 #endif
53
54 #if defined(QT5)
55   setRenderTarget(QQuickPaintedItem::FramebufferObject);
56   setSmooth(false);
57   setAntialiasing(false);
58 #endif
59 }
60
61 VideoPlayer::~VideoPlayer() {
62   stop();
63
64   if (m_bin) {
65     gst_object_unref(m_bin);
66     m_bin = 0;
67   }
68 }
69
70 void VideoPlayer::componentComplete() {
71   if (!m_config) {
72     qmlInfo(this) << "CameraConfig not set";
73     return;
74   }
75
76   m_renderer = QtCamViewfinderRenderer::create(m_config->config(), this);
77   if (!m_renderer) {
78     qmlInfo(this) << "Failed to create viewfinder renderer";
79     return;
80   }
81
82   m_renderer->resize(QSizeF(width(), height()));
83   QObject::connect(m_renderer, SIGNAL(updateRequested()), this, SLOT(updateRequested()));
84
85   if (m_bin) {
86     g_object_set(m_bin, "video-sink", m_renderer->sinkElement(), NULL);
87   }
88 }
89
90 void VideoPlayer::classBegin() {
91   m_bin = gst_element_factory_make ("playbin2", "VideoPlayerBin");
92   if (!m_bin) {
93     qmlInfo(this) << "Failed to create playbin2";
94     return;
95   }
96
97   g_object_set (m_bin, "flags", 99, NULL);
98
99   GstElement *elem = gst_element_factory_make("pulsesink", "VideoPlayerPulseSink");
100   if (!elem) {
101     qmlInfo(this) << "Failed to create pulsesink";
102   }
103   else {
104     // TODO: properties on sink
105     g_object_set (m_bin, "audio-sink", elem, NULL);
106   }
107
108   GstBus *bus = gst_element_get_bus(m_bin);
109   gst_bus_add_watch(bus, bus_call, this);
110   gst_object_unref(bus);
111 }
112
113 QUrl VideoPlayer::source() const {
114   return m_url;
115 }
116
117 void VideoPlayer::setSource(const QUrl& source) {
118   if (m_url != source) {
119     m_url = source;
120     emit sourceChanged();
121   }
122 }
123
124 CameraConfig *VideoPlayer::cameraConfig() const {
125   return m_config;  
126 }
127
128 void VideoPlayer::setCameraConfig(CameraConfig *config) {
129   if (m_config && m_config != config) {
130     qmlInfo(this) << "Cannot reset CameraConfig";
131     return;
132   }
133
134   if (!config) {
135     qmlInfo(this) << "CameraConfig cannot be empty";
136     return;
137   }
138
139   if (m_config != config) {
140     m_config = config;
141     emit cameraConfigChanged();
142   }
143 }
144
145 qint64 VideoPlayer::duration() const {
146   if (!m_bin) {
147     return 0;
148   }
149
150   GstFormat format = GST_FORMAT_TIME;
151   qint64 dur = 0;
152   if (!gst_element_query_duration(m_bin, &format, &dur)) {
153     qmlInfo(this) << "Failed to query pipeline duration";
154     return 0;
155   }
156
157   if (format != GST_FORMAT_TIME) {
158     qmlInfo(this) << "Pipeline format is not time";
159     return 0;
160   }
161
162   dur /= 1000000;
163
164   return dur;
165 }
166
167 qint64 VideoPlayer::position() {
168   if (!m_bin) {
169     return 0;
170   }
171
172   GstFormat format = GST_FORMAT_TIME;
173   qint64 pos = 0;
174   if (!gst_element_query_position(m_bin, &format, &pos)) {
175     qmlInfo(this) << "Failed to query pipeline position";
176     return m_pos;
177   }
178
179   if (format != GST_FORMAT_TIME) {
180     qmlInfo(this) << "Pipeline format is not time";
181     return m_pos;
182   }
183
184   pos /= 1000000;
185
186   m_pos = pos;
187
188   return pos;
189 }
190
191 void VideoPlayer::setPosition(qint64 position) {
192   seek(position);
193 }
194
195 bool VideoPlayer::pause() {
196   return setState(VideoPlayer::StatePaused);
197 }
198
199 bool VideoPlayer::play() {
200   return setState(VideoPlayer::StatePlaying);
201 }
202
203 bool VideoPlayer::seek(qint64 offset) {
204   if (!m_bin) {
205     qmlInfo(this) << "no playbin2";
206     return false;
207   }
208
209   qint64 pos = offset;
210
211   offset *= 1000000;
212
213   GstSeekFlags flags = (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE);
214   gboolean ret = gst_element_seek_simple (m_bin, GST_FORMAT_TIME,
215                                           flags, offset);
216
217   if (ret) {
218     m_pos = pos;
219
220     return TRUE;
221   }
222
223   return FALSE;
224 }
225
226 bool VideoPlayer::stop() {
227   return setState(VideoPlayer::StateStopped);
228 }
229
230 void VideoPlayer::geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry) {
231 #if defined(QT4)
232   QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
233 #elif defined(QT5)
234   QQuickPaintedItem::geometryChanged(newGeometry, oldGeometry);
235 #endif
236
237   if (m_renderer) {
238     m_renderer->resize(newGeometry.size());
239   }
240 }
241
242 #if defined(QT4)
243 void VideoPlayer::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
244                        QWidget *widget) {
245
246   Q_UNUSED(widget);
247   Q_UNUSED(option);
248
249   painter->fillRect(boundingRect(), Qt::black);
250
251 #elif defined(QT5)
252 void VideoPlayer::paint(QPainter *painter) {
253   painter->fillRect(contentsBoundingRect(), Qt::black);
254
255 #endif
256
257   if (!m_renderer) {
258     return;
259   }
260
261   m_renderer->paint(painter);
262 }
263
264 VideoPlayer::State VideoPlayer::state() const {
265   return m_state;
266 }
267
268 bool VideoPlayer::setState(const VideoPlayer::State& state) {
269   if (state == m_state) {
270     return true;
271   }
272
273   if (!m_bin) {
274     qmlInfo(this) << "no playbin2";
275     return false;
276   }
277
278   if (state == VideoPlayer::StatePaused) {
279     m_timer->stop();
280
281     // Set uri if needed:
282     if (m_state == VideoPlayer::StateStopped) {
283       const char *uri = m_url.toString().toUtf8().constData();
284       g_object_set(m_bin, "uri", uri, NULL);
285     }
286
287     if (gst_element_set_state(m_bin, GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) {
288       qmlInfo(this) << "error setting pipeline to PAUSED";
289       return false;
290     }
291
292     GstState st;
293     if (gst_element_get_state(m_bin, &st, NULL, GST_CLOCK_TIME_NONE)
294         == GST_STATE_CHANGE_FAILURE) {
295       qmlInfo(this) << "setting pipeline to PAUSED failed";
296       return false;
297     }
298
299     if (st != GST_STATE_PAUSED) {
300       qmlInfo(this) << "pipeline failed to transition to to PAUSED state";
301       return false;
302     }
303
304     m_state = state;
305     emit stateChanged();
306
307     return true;
308   }
309   else if (state == VideoPlayer::StatePlaying) {
310     // Set uri if needed:
311     if (m_state == VideoPlayer::StateStopped) {
312       const char *uri = m_url.toString().toUtf8().constData();
313       g_object_set(m_bin, "uri", uri, NULL);
314     }
315
316     if (gst_element_set_state(m_bin, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
317       qmlInfo(this) << "error setting pipeline to PLAYING";
318       return false;
319     }
320
321     GstState st;
322     if (gst_element_get_state(m_bin, &st, NULL, GST_CLOCK_TIME_NONE)
323         == GST_STATE_CHANGE_FAILURE) {
324       qmlInfo(this) << "setting pipeline to PLAYING failed";
325       return false;
326     }
327
328     if (st != GST_STATE_PLAYING) {
329       qmlInfo(this) << "pipeline failed to transition to to PLAYING state";
330       return false;
331     }
332
333     m_state = state;
334     emit stateChanged();
335
336     emit durationChanged();
337     emit positionChanged();
338
339     m_timer->start();
340     return true;
341   }
342   else {
343     m_timer->stop();
344     m_pos = 0;
345
346     if (gst_element_set_state(m_bin, GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE) {
347       qmlInfo(this) << "error setting pipeline to NULL";
348       return false;
349     }
350
351     GstState st;
352     if (gst_element_get_state(m_bin, &st, NULL, GST_CLOCK_TIME_NONE)
353         == GST_STATE_CHANGE_FAILURE) {
354       qmlInfo(this) << "setting pipeline to NULL failed";
355       return false;
356     }
357
358     if (st != GST_STATE_NULL) {
359       qmlInfo(this) << "pipeline failed to transition to to NULL state";
360       return false;
361     }
362
363     m_state = state;
364     emit stateChanged();
365
366     emit durationChanged();
367     emit positionChanged();
368
369     return true;
370   }
371 }
372
373 gboolean VideoPlayer::bus_call(GstBus *bus, GstMessage *msg, gpointer data) {
374   Q_UNUSED(bus);
375
376   VideoPlayer *that = (VideoPlayer *) data;
377
378   gchar *debug = NULL;
379   GError *err = NULL;
380
381   switch (GST_MESSAGE_TYPE(msg)) {
382   case GST_MESSAGE_EOS:
383     that->stop();
384     break;
385
386   case GST_MESSAGE_ERROR:
387     gst_message_parse_error (msg, &err, &debug);
388
389     emit that->error(err->message, err->code, debug);
390     that->stop();
391     if (err) {
392       g_error_free (err);
393     }
394
395     if (debug) {
396       g_free (debug);
397     }
398
399     break;
400
401   default:
402     break;
403   }
404
405   return TRUE;  
406 }
407
408 void VideoPlayer::updateRequested() {
409   update();
410 }