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