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