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