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