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