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