No need to set slider orientation. Default is already horizontal
[harmattan/cameraplus] / lib / qtcamdevice.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 "qtcamdevice.h"
22 #include "qtcamviewfinder.h"
23 #include "qtcamconfig.h"
24 #include "qtcamdevice_p.h"
25 #include <QDebug>
26 #include <gst/gst.h>
27 #include "qtcamgstreamermessagelistener.h"
28 #include "qtcammode.h"
29 #include "qtcamimagemode.h"
30 #include "qtcamvideomode.h"
31 #include "qtcamnotifications.h"
32 #include "qtcampropertysetter.h"
33 #include "qtcamanalysisbin.h"
34
35 QtCamDevice::QtCamDevice(QtCamConfig *config, const QString& name,
36                          const QVariant& id, QObject *parent) :
37   QObject(parent), d_ptr(new QtCamDevicePrivate) {
38
39   d_ptr->q_ptr = this;
40   d_ptr->name = name;
41   d_ptr->id = id;
42   d_ptr->conf = config;
43
44   d_ptr->cameraBin = gst_element_factory_make("camerabin2", "QtCameraCameraBin");
45   if (!d_ptr->cameraBin) {
46     qCritical() << "Failed to create camerabin";
47     return;
48   }
49
50   d_ptr->propertySetter = new QtCamPropertySetter(d_ptr);
51
52   d_ptr->createAndAddElement(d_ptr->conf->audioSource(), "audio-source", "QtCameraAudioSrc");
53   if (!d_ptr->conf->wrapperVideoSource().isEmpty() &&
54       !d_ptr->conf->wrapperVideoSourceProperty().isEmpty()) {
55     d_ptr->createAndAddVideoSourceAndWrapper();
56   }
57   else {
58     d_ptr->createAndAddVideoSource();
59   }
60
61   d_ptr->setDevicePoperty();
62
63   int flags =
64     0x00000001 /* no-audio-conversion - Do not use audio conversion elements */
65     | 0x00000002 /* no-video-conversion - Do not use video conversion elements */
66     | 0x00000004 /* no-viewfinder-conversion - Do not use viewfinder conversion elements */
67     | 0x00000008; /* no-image-conversion - Do not use image conversion elements */
68
69   g_object_set(d_ptr->cameraBin, "flags", flags, NULL);
70
71   d_ptr->setAudioCaptureCaps();
72
73   QStringList viewfinderFilters = d_ptr->conf->viewfinderFilters();
74   if (!viewfinderFilters.isEmpty()) {
75     d_ptr->viewfinderFilters =
76       QtCamAnalysisBin::create(viewfinderFilters, "QtCamViewfinderFilters");
77
78     if (!d_ptr->viewfinderFilters) {
79       qWarning() << "Failed to create viewfinder filters";
80     }
81     else {
82       g_object_set(d_ptr->cameraBin, "viewfinder-filter", d_ptr->viewfinderFilters->bin(), NULL);
83     }
84   }
85
86   d_ptr->listener = new QtCamGStreamerMessageListener(gst_element_get_bus(d_ptr->cameraBin),
87                                                       d_ptr, this);
88
89   QObject::connect(d_ptr->listener, SIGNAL(error(const QString&, int, const QString&)),
90                    this, SLOT(_d_error(const QString&, int, const QString&)));
91   QObject::connect(d_ptr->listener, SIGNAL(started()), this, SLOT(_d_started()));
92   QObject::connect(d_ptr->listener, SIGNAL(stopped()), this, SLOT(_d_stopped()));
93   QObject::connect(d_ptr->listener, SIGNAL(stopping()), this, SLOT(_d_stopping()));
94
95   g_signal_connect(d_ptr->cameraBin, "notify::idle",
96                    G_CALLBACK(QtCamDevicePrivate::on_idle_changed), d_ptr);
97
98   if (d_ptr->wrapperVideoSource) {
99     g_signal_connect(d_ptr->wrapperVideoSource, "notify::ready-for-capture",
100                      G_CALLBACK(QtCamDevicePrivate::on_ready_for_capture_changed), d_ptr);
101   }
102   else {
103     g_signal_connect(d_ptr->videoSource, "notify::ready-for-capture",
104                      G_CALLBACK(QtCamDevicePrivate::on_ready_for_capture_changed), d_ptr);
105   }
106
107   d_ptr->image = new QtCamImageMode(d_ptr, this);
108   d_ptr->video = new QtCamVideoMode(d_ptr, this);
109
110   d_ptr->notifications = new QtCamNotifications(this, this);
111 }
112
113 QtCamDevice::~QtCamDevice() {
114   stop(true);
115
116   d_ptr->image->deactivate();
117   d_ptr->video->deactivate();
118
119   delete d_ptr->image; d_ptr->image = 0;
120   delete d_ptr->video; d_ptr->video = 0;
121
122   delete d_ptr->propertySetter;
123
124   delete d_ptr->viewfinderFilters;
125
126   if (d_ptr->cameraBin) {
127     gst_object_unref(d_ptr->cameraBin);
128   }
129
130   delete d_ptr; d_ptr = 0;
131 }
132
133 bool QtCamDevice::setViewfinder(QtCamViewfinder *viewfinder) {
134   if (isRunning()) {
135     qWarning() << "QtCamDevice: pipeline must be stopped before setting a viewfinder";
136     return false;
137   }
138
139   if (d_ptr->viewfinder == viewfinder) {
140     return true;
141   }
142
143   if (!viewfinder) {
144     if (d_ptr->cameraBin) {
145       g_object_set(d_ptr->cameraBin, "viewfinder-sink", NULL, NULL);
146     }
147
148     d_ptr->sink = 0;
149
150     d_ptr->viewfinder = 0;
151     return true;
152   }
153
154   d_ptr->viewfinder = viewfinder;
155   d_ptr->sink = 0;
156
157   return true;
158 }
159
160 QtCamViewfinder *QtCamDevice::viewfinder() const {
161   return d_ptr->viewfinder;
162 }
163
164 bool QtCamDevice::start() {
165   if (d_ptr->error) {
166     qWarning() << "Pipeline must be stopped first because of an error.";
167     return false;
168   }
169
170   if (!d_ptr->cameraBin) {
171     qWarning() << "Missing camerabin";
172     return false;
173   }
174
175   if (!d_ptr->viewfinder) {
176     qWarning() << "Viewfinder not set";
177     return false;
178   }
179
180   if (isRunning()) {
181     return true;
182   }
183
184   if (!d_ptr->active) {
185     d_ptr->image->activate();
186   }
187   else {
188     d_ptr->active->applySettings();
189   }
190
191   // Set sink.
192   if (!d_ptr->setViewfinderSink()) {
193     return false;
194   }
195
196   GstStateChangeReturn err = gst_element_set_state(d_ptr->cameraBin, GST_STATE_PLAYING);
197   if (err == GST_STATE_CHANGE_FAILURE) {
198     qWarning() << "Failed to start camera pipeline";
199     return false;
200   }
201
202   // We need to wait for startup to complet. There's a race condition somewhere in the pipeline.
203   // If we set the scene mode to night and update the resolution while starting up
204   // then subdevsrc2 barfs:
205   // streaming task paused, reason not-negotiated (-4)
206   GstState state;
207   if (err != GST_STATE_CHANGE_ASYNC) {
208     return true;
209   }
210
211   if (gst_element_get_state(d_ptr->cameraBin, &state, 0, GST_CLOCK_TIME_NONE)
212       != GST_STATE_CHANGE_SUCCESS) {
213     // We are seriously screwed up :(
214     return false;
215   }
216
217   if (state != GST_STATE_PLAYING) {
218     // Huh ? Is this even possible ??
219     return false;
220   }
221
222   return true;
223 }
224
225 bool QtCamDevice::stop(bool force) {
226   if (!d_ptr->cameraBin) {
227     return true;
228   }
229
230   if (d_ptr->error) {
231     gst_element_set_state(d_ptr->cameraBin, GST_STATE_NULL);
232     d_ptr->error = false;
233
234     d_ptr->viewfinder->stop();
235
236     return true;
237   }
238
239   GstState state;
240   gst_element_get_state(d_ptr->cameraBin, &state, 0, GST_CLOCK_TIME_NONE);
241
242   if (state == GST_STATE_NULL) {
243     // Nothing to do.
244     return true;
245   }
246
247   if (!isIdle()) {
248     if (!force) {
249       return false;
250     }
251   }
252
253   // First we go to ready:
254   GstStateChangeReturn st = gst_element_set_state(d_ptr->cameraBin, GST_STATE_READY);
255   if (st != GST_STATE_CHANGE_FAILURE) {
256     // Flush the bus:
257     d_ptr->listener->flushMessages();
258   }
259
260   // Now to NULL
261   gst_element_set_state(d_ptr->cameraBin, GST_STATE_NULL);
262
263   d_ptr->viewfinder->stop();
264
265   return true;
266 }
267
268 bool QtCamDevice::isRunning() {
269   if (!d_ptr->cameraBin) {
270     return false;
271   }
272
273   GstState state;
274   GstStateChangeReturn err = gst_element_get_state(d_ptr->cameraBin,
275                                                    &state, 0, GST_CLOCK_TIME_NONE);
276
277   if (err == GST_STATE_CHANGE_FAILURE || state != GST_STATE_PLAYING) {
278     return false;
279   }
280
281   return true;
282 }
283
284 bool QtCamDevice::isIdle() {
285   if (!d_ptr->cameraBin) {
286     return true;
287   }
288
289   gboolean idle = FALSE;
290   g_object_get(d_ptr->cameraBin, "idle", &idle, NULL);
291
292   return idle == TRUE;
293 }
294
295 QtCamImageMode *QtCamDevice::imageMode() const {
296   return d_ptr->image;
297 }
298
299 QtCamVideoMode *QtCamDevice::videoMode() const {
300   return d_ptr->video;
301 }
302
303 QtCamMode *QtCamDevice::activeMode() const {
304   return d_ptr->active;
305 }
306
307 QString QtCamDevice::name() const {
308   return d_ptr->name;
309 }
310
311 QVariant QtCamDevice::id() const {
312   return d_ptr->id;
313 }
314
315 QtCamConfig *QtCamDevice::config() const {
316   return d_ptr->conf;
317 }
318
319 QtCamGStreamerMessageListener *QtCamDevice::listener() const {
320   return d_ptr->listener;
321 }
322
323 QtCamNotifications *QtCamDevice::notifications() const {
324   return d_ptr->notifications;
325 }
326
327 #include "moc_qtcamdevice.cpp"