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