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