Call parent class componentComplete()
[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 defined(QT4)
72   QDeclarativeItem::componentComplete();
73 #elif defined(QT5)
74   QQuickPaintedItem::componentComplete();
75 #endif
76
77   if (!m_config) {
78     qmlInfo(this) << "CameraConfig not set";
79     return;
80   }
81
82   m_renderer = QtCamViewfinderRenderer::create(m_config->config(), this);
83   if (!m_renderer) {
84     qmlInfo(this) << "Failed to create viewfinder renderer";
85     return;
86   }
87
88   m_renderer->resize(QSizeF(width(), height()));
89   QObject::connect(m_renderer, SIGNAL(updateRequested()), this, SLOT(updateRequested()));
90
91   if (m_bin) {
92     g_object_set(m_bin, "video-sink", m_renderer->sinkElement(), NULL);
93   }
94 }
95
96 void VideoPlayer::classBegin() {
97   m_bin = gst_element_factory_make ("playbin2", "VideoPlayerBin");
98   if (!m_bin) {
99     qmlInfo(this) << "Failed to create playbin2";
100     return;
101   }
102
103   g_object_set (m_bin, "flags", 99, NULL);
104
105   GstElement *elem = gst_element_factory_make("pulsesink", "VideoPlayerPulseSink");
106   if (!elem) {
107     qmlInfo(this) << "Failed to create pulsesink";
108   }
109   else {
110     // TODO: properties on sink
111     g_object_set (m_bin, "audio-sink", elem, NULL);
112   }
113
114   GstBus *bus = gst_element_get_bus(m_bin);
115   gst_bus_add_watch(bus, bus_call, this);
116   gst_object_unref(bus);
117 }
118
119 QUrl VideoPlayer::source() const {
120   return m_url;
121 }
122
123 void VideoPlayer::setSource(const QUrl& source) {
124   if (m_url != source) {
125     m_url = source;
126     emit sourceChanged();
127   }
128 }
129
130 CameraConfig *VideoPlayer::cameraConfig() const {
131   return m_config;  
132 }
133
134 void VideoPlayer::setCameraConfig(CameraConfig *config) {
135   if (m_config && m_config != config) {
136     qmlInfo(this) << "Cannot reset CameraConfig";
137     return;
138   }
139
140   if (!config) {
141     qmlInfo(this) << "CameraConfig cannot be empty";
142     return;
143   }
144
145   if (m_config != config) {
146     m_config = config;
147     emit cameraConfigChanged();
148   }
149 }
150
151 qint64 VideoPlayer::duration() const {
152   if (!m_bin) {
153     return 0;
154   }
155
156   GstFormat format = GST_FORMAT_TIME;
157   qint64 dur = 0;
158   if (!gst_element_query_duration(m_bin, &format, &dur)) {
159     qmlInfo(this) << "Failed to query pipeline duration";
160     return 0;
161   }
162
163   if (format != GST_FORMAT_TIME) {
164     qmlInfo(this) << "Pipeline format is not time";
165     return 0;
166   }
167
168   dur /= 1000000;
169
170   return dur;
171 }
172
173 qint64 VideoPlayer::position() {
174   if (!m_bin) {
175     return 0;
176   }
177
178   GstFormat format = GST_FORMAT_TIME;
179   qint64 pos = 0;
180   if (!gst_element_query_position(m_bin, &format, &pos)) {
181     qmlInfo(this) << "Failed to query pipeline position";
182     return m_pos;
183   }
184
185   if (format != GST_FORMAT_TIME) {
186     qmlInfo(this) << "Pipeline format is not time";
187     return m_pos;
188   }
189
190   pos /= 1000000;
191
192   m_pos = pos;
193
194   return pos;
195 }
196
197 void VideoPlayer::setPosition(qint64 position) {
198   seek(position);
199 }
200
201 bool VideoPlayer::pause() {
202   return setState(VideoPlayer::StatePaused);
203 }
204
205 bool VideoPlayer::play() {
206   return setState(VideoPlayer::StatePlaying);
207 }
208
209 bool VideoPlayer::seek(qint64 offset) {
210   if (!m_bin) {
211     qmlInfo(this) << "no playbin2";
212     return false;
213   }
214
215   qint64 pos = offset;
216
217   offset *= 1000000;
218
219   GstSeekFlags flags = (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE);
220   gboolean ret = gst_element_seek_simple (m_bin, GST_FORMAT_TIME,
221                                           flags, offset);
222
223   if (ret) {
224     m_pos = pos;
225
226     return TRUE;
227   }
228
229   return FALSE;
230 }
231
232 bool VideoPlayer::stop() {
233   return setState(VideoPlayer::StateStopped);
234 }
235
236 void VideoPlayer::geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry) {
237 #if defined(QT4)
238   QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
239 #elif defined(QT5)
240   QQuickPaintedItem::geometryChanged(newGeometry, oldGeometry);
241 #endif
242
243   if (m_renderer) {
244     m_renderer->resize(newGeometry.size());
245   }
246 }
247
248 #if defined(QT4)
249 void VideoPlayer::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
250                        QWidget *widget) {
251
252   Q_UNUSED(widget);
253   Q_UNUSED(option);
254
255   painter->fillRect(boundingRect(), Qt::black);
256
257 #elif defined(QT5)
258 void VideoPlayer::paint(QPainter *painter) {
259   painter->fillRect(contentsBoundingRect(), Qt::black);
260
261 #endif
262
263   if (!m_renderer) {
264     return;
265   }
266
267   bool needsNativePainting = m_renderer->needsNativePainting();
268
269   if (needsNativePainting) {
270     painter->beginNativePainting();
271   }
272
273   m_renderer->paint(QMatrix4x4(painter->combinedTransform()), painter->viewport());
274
275   if (needsNativePainting) {
276     painter->endNativePainting();
277   }
278 }
279
280 VideoPlayer::State VideoPlayer::state() const {
281   return m_state;
282 }
283
284 bool VideoPlayer::setState(const VideoPlayer::State& state) {
285   if (state == m_state) {
286     return true;
287   }
288
289   if (!m_bin) {
290     qmlInfo(this) << "no playbin2";
291     return false;
292   }
293
294   if (state == VideoPlayer::StatePaused) {
295     m_timer->stop();
296
297     // Set uri if needed:
298     if (m_state == VideoPlayer::StateStopped) {
299       const char *uri = m_url.toString().toUtf8().constData();
300       g_object_set(m_bin, "uri", uri, NULL);
301     }
302
303     if (gst_element_set_state(m_bin, GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) {
304       qmlInfo(this) << "error setting pipeline to PAUSED";
305       return false;
306     }
307
308     GstState st;
309     if (gst_element_get_state(m_bin, &st, NULL, GST_CLOCK_TIME_NONE)
310         == GST_STATE_CHANGE_FAILURE) {
311       qmlInfo(this) << "setting pipeline to PAUSED failed";
312       return false;
313     }
314
315     if (st != GST_STATE_PAUSED) {
316       qmlInfo(this) << "pipeline failed to transition to to PAUSED state";
317       return false;
318     }
319
320     m_state = state;
321     emit stateChanged();
322
323     return true;
324   }
325   else if (state == VideoPlayer::StatePlaying) {
326     // Set uri if needed:
327     if (m_state == VideoPlayer::StateStopped) {
328       const char *uri = m_url.toString().toUtf8().constData();
329       g_object_set(m_bin, "uri", uri, NULL);
330     }
331
332     if (gst_element_set_state(m_bin, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
333       qmlInfo(this) << "error setting pipeline to PLAYING";
334       return false;
335     }
336
337     GstState st;
338     if (gst_element_get_state(m_bin, &st, NULL, GST_CLOCK_TIME_NONE)
339         == GST_STATE_CHANGE_FAILURE) {
340       qmlInfo(this) << "setting pipeline to PLAYING failed";
341       return false;
342     }
343
344     if (st != GST_STATE_PLAYING) {
345       qmlInfo(this) << "pipeline failed to transition to to PLAYING state";
346       return false;
347     }
348
349     m_state = state;
350     emit stateChanged();
351
352     emit durationChanged();
353     emit positionChanged();
354
355     m_timer->start();
356     return true;
357   }
358   else {
359     m_timer->stop();
360     m_pos = 0;
361
362     if (gst_element_set_state(m_bin, GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE) {
363       qmlInfo(this) << "error setting pipeline to NULL";
364       return false;
365     }
366
367     GstState st;
368     if (gst_element_get_state(m_bin, &st, NULL, GST_CLOCK_TIME_NONE)
369         == GST_STATE_CHANGE_FAILURE) {
370       qmlInfo(this) << "setting pipeline to NULL failed";
371       return false;
372     }
373
374     if (st != GST_STATE_NULL) {
375       qmlInfo(this) << "pipeline failed to transition to to NULL state";
376       return false;
377     }
378
379     m_state = state;
380     emit stateChanged();
381
382     emit durationChanged();
383     emit positionChanged();
384
385     return true;
386   }
387 }
388
389 gboolean VideoPlayer::bus_call(GstBus *bus, GstMessage *msg, gpointer data) {
390   Q_UNUSED(bus);
391
392   VideoPlayer *that = (VideoPlayer *) data;
393
394   gchar *debug = NULL;
395   GError *err = NULL;
396
397   switch (GST_MESSAGE_TYPE(msg)) {
398   case GST_MESSAGE_EOS:
399     that->stop();
400     break;
401
402   case GST_MESSAGE_ERROR:
403     gst_message_parse_error (msg, &err, &debug);
404
405     emit that->error(err->message, err->code, debug);
406     that->stop();
407     if (err) {
408       g_error_free (err);
409     }
410
411     if (debug) {
412       g_free (debug);
413     }
414
415     break;
416
417   default:
418     break;
419   }
420
421   return TRUE;  
422 }
423
424 void VideoPlayer::updateRequested() {
425   update();
426 }