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