2 * This file is part of CameraPlus.
4 * Copyright (C) 2012-2013 Mohammed Sameer <msameer@foolab.org>
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.
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.
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
21 #include "videoplayer.h"
23 #include <QDeclarativeInfo>
27 #include "cameraconfig.h"
29 #include "qtcamviewfinderrenderer.h"
35 VideoPlayer::VideoPlayer(QDeclarativeItem *parent) :
36 QDeclarativeItem(parent),
38 VideoPlayer::VideoPlayer(QQuickItem *parent) :
39 QQuickPaintedItem(parent),
44 m_state(VideoPlayer::StateStopped),
45 m_timer(new QTimer(this)),
48 m_timer->setSingleShot(false);
49 m_timer->setInterval(500);
50 QObject::connect(m_timer, SIGNAL(timeout()), this, SIGNAL(positionChanged()));
53 setFlag(QGraphicsItem::ItemHasNoContents, false);
57 setRenderTarget(QQuickPaintedItem::FramebufferObject);
59 setAntialiasing(false);
63 VideoPlayer::~VideoPlayer() {
67 gst_object_unref(m_bin);
72 void VideoPlayer::componentComplete() {
74 QDeclarativeItem::componentComplete();
76 QQuickPaintedItem::componentComplete();
80 qmlInfo(this) << "CameraConfig not set";
84 m_renderer = QtCamViewfinderRenderer::create(m_config->config(), this);
86 qmlInfo(this) << "Failed to create viewfinder renderer";
90 m_renderer->resize(QSizeF(width(), height()));
91 QObject::connect(m_renderer, SIGNAL(updateRequested()), this, SLOT(updateRequested()));
94 g_object_set(m_bin, "video-sink", m_renderer->sinkElement(), NULL);
98 void VideoPlayer::classBegin() {
100 QDeclarativeItem::classBegin();
102 QQuickPaintedItem::classBegin();
105 m_bin = gst_element_factory_make ("playbin2", "VideoPlayerBin");
107 qmlInfo(this) << "Failed to create playbin2";
111 g_signal_connect (G_OBJECT (m_bin), "notify::volume", G_CALLBACK (on_volume_changed), this);
112 g_object_set (m_bin, "flags", 99, NULL);
114 GstElement *elem = gst_element_factory_make("pulsesink", "VideoPlayerPulseSink");
116 qmlInfo(this) << "Failed to create pulsesink";
119 g_object_set (m_bin, "audio-sink", elem, NULL);
122 GstBus *bus = gst_element_get_bus(m_bin);
123 gst_bus_add_watch(bus, bus_call, this);
124 gst_object_unref(bus);
127 QUrl VideoPlayer::source() const {
131 void VideoPlayer::setSource(const QUrl& source) {
132 if (m_url != source) {
134 emit sourceChanged();
138 CameraConfig *VideoPlayer::cameraConfig() const {
142 void VideoPlayer::setCameraConfig(CameraConfig *config) {
143 if (m_config && m_config != config) {
144 qmlInfo(this) << "Cannot reset CameraConfig";
149 qmlInfo(this) << "CameraConfig cannot be empty";
153 if (m_config != config) {
155 emit cameraConfigChanged();
159 qint64 VideoPlayer::duration() const {
164 GstFormat format = GST_FORMAT_TIME;
166 if (!gst_element_query_duration(m_bin, &format, &dur)) {
167 qmlInfo(this) << "Failed to query pipeline duration";
171 if (format != GST_FORMAT_TIME) {
172 qmlInfo(this) << "Pipeline format is not time";
181 qint64 VideoPlayer::position() {
186 GstFormat format = GST_FORMAT_TIME;
188 if (!gst_element_query_position(m_bin, &format, &pos)) {
189 qmlInfo(this) << "Failed to query pipeline position";
193 if (format != GST_FORMAT_TIME) {
194 qmlInfo(this) << "Pipeline format is not time";
205 void VideoPlayer::setPosition(qint64 position) {
209 bool VideoPlayer::pause() {
210 return setState(VideoPlayer::StatePaused);
213 bool VideoPlayer::play() {
214 return setState(VideoPlayer::StatePlaying);
217 bool VideoPlayer::seek(qint64 offset) {
219 qmlInfo(this) << "no playbin2";
227 GstSeekFlags flags = (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE);
228 gboolean ret = gst_element_seek_simple (m_bin, GST_FORMAT_TIME,
240 bool VideoPlayer::stop() {
241 return setState(VideoPlayer::StateStopped);
244 void VideoPlayer::geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry) {
246 QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
248 QQuickPaintedItem::geometryChanged(newGeometry, oldGeometry);
252 m_renderer->resize(newGeometry.size());
257 void VideoPlayer::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
263 painter->fillRect(boundingRect(), Qt::black);
266 void VideoPlayer::paint(QPainter *painter) {
267 painter->fillRect(contentsBoundingRect(), Qt::black);
275 bool needsNativePainting = m_renderer->needsNativePainting();
277 if (needsNativePainting) {
278 painter->beginNativePainting();
281 m_renderer->paint(QMatrix4x4(painter->combinedTransform()), painter->viewport());
283 if (needsNativePainting) {
284 painter->endNativePainting();
288 VideoPlayer::State VideoPlayer::state() const {
292 bool VideoPlayer::setState(const VideoPlayer::State& state) {
293 if (state == m_state) {
298 qmlInfo(this) << "no playbin2";
302 if (state == VideoPlayer::StatePaused) {
305 // Set uri if needed:
306 if (m_state == VideoPlayer::StateStopped) {
307 const char *uri = m_url.toString().toUtf8().constData();
308 g_object_set(m_bin, "uri", uri, NULL);
311 if (gst_element_set_state(m_bin, GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) {
312 qmlInfo(this) << "error setting pipeline to PAUSED";
317 if (gst_element_get_state(m_bin, &st, NULL, GST_CLOCK_TIME_NONE)
318 == GST_STATE_CHANGE_FAILURE) {
319 qmlInfo(this) << "setting pipeline to PAUSED failed";
323 if (st != GST_STATE_PAUSED) {
324 qmlInfo(this) << "pipeline failed to transition to to PAUSED state";
333 else if (state == VideoPlayer::StatePlaying) {
334 // Set uri if needed:
335 if (m_state == VideoPlayer::StateStopped) {
336 const char *uri = m_url.toString().toUtf8().constData();
337 g_object_set(m_bin, "uri", uri, NULL);
340 if (gst_element_set_state(m_bin, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
341 qmlInfo(this) << "error setting pipeline to PLAYING";
346 if (gst_element_get_state(m_bin, &st, NULL, GST_CLOCK_TIME_NONE)
347 == GST_STATE_CHANGE_FAILURE) {
348 qmlInfo(this) << "setting pipeline to PLAYING failed";
352 if (st != GST_STATE_PLAYING) {
353 qmlInfo(this) << "pipeline failed to transition to to PLAYING state";
360 emit durationChanged();
361 emit positionChanged();
370 if (gst_element_set_state(m_bin, GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE) {
371 qmlInfo(this) << "error setting pipeline to NULL";
376 if (gst_element_get_state(m_bin, &st, NULL, GST_CLOCK_TIME_NONE)
377 == GST_STATE_CHANGE_FAILURE) {
378 qmlInfo(this) << "setting pipeline to NULL failed";
382 if (st != GST_STATE_NULL) {
383 qmlInfo(this) << "pipeline failed to transition to to NULL state";
390 emit durationChanged();
391 emit positionChanged();
397 gboolean VideoPlayer::bus_call(GstBus *bus, GstMessage *msg, gpointer data) {
400 VideoPlayer *that = (VideoPlayer *) data;
405 switch (GST_MESSAGE_TYPE(msg)) {
406 case GST_MESSAGE_EOS:
410 case GST_MESSAGE_ERROR:
411 gst_message_parse_error (msg, &err, &debug);
413 emit that->error(err->message, err->code, debug);
432 void VideoPlayer::updateRequested() {
436 quint32 VideoPlayer::volume() {
438 g_object_get (m_bin, "volume", &vol, NULL);
440 qint32 res = (int)round(vol * 100.0);
445 void VideoPlayer::setVolume(quint32 volume) {
446 if (VideoPlayer::volume() != volume) {
447 double vol = volume / 100.0;
448 g_object_set (m_bin, "volume", vol, NULL);
449 emit volumeChanged();
453 void VideoPlayer::on_volume_changed(GObject *object, GParamSpec *pspec, gpointer user_data) {
457 VideoPlayer *player = (VideoPlayer *) user_data;
459 QMetaObject::invokeMethod(player, "volumeChanged", Qt::QueuedConnection);