Race condition when QtCamVideo::stop() gets called multiple times.
[harmattan/cameraplus] / lib / qtcamvideomode.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 "qtcamvideomode.h"
22 #include "qtcammode_p.h"
23 #include <QDebug>
24 #include "qtcamdevice_p.h"
25 #include "qtcamdevice.h"
26 #include "qtcamvideosettings.h"
27 #include "qtcamnotifications.h"
28 #include <QMutex>
29 #include <QWaitCondition>
30
31 class QtCamVideoModePrivate : public QtCamModePrivate {
32 public:
33   QtCamVideoModePrivate(QtCamDevicePrivate *dev) :
34   QtCamModePrivate(dev),
35   settings(dev->conf->videoSettings(dev->id)),
36   resolution(settings->defaultResolution()) {
37
38   }
39
40   ~QtCamVideoModePrivate() {
41     delete settings;
42   }
43
44   void _d_idleStateChanged(bool isIdle) {
45     if (isIdle && dev->active == dev->video) {
46       QMetaObject::invokeMethod(dev->video, "recordingStateChanged");
47       QMetaObject::invokeMethod(dev->video, "canCaptureChanged");
48     }
49   }
50
51   QtCamVideoSettings *settings;
52   QtCamVideoResolution resolution;
53 };
54
55 class VideoDoneHandler : public DoneHandler {
56 public:
57   VideoDoneHandler(QtCamModePrivate *d, QObject *parent = 0) :
58     DoneHandler(d, "video-done", parent), m_done(false) {}
59
60   virtual void handleMessage(GstMessage *message) {
61     DoneHandler::handleMessage(message);
62     wake();
63   }
64
65   void lock() {
66     m_mutex.lock();
67   }
68
69   void unlock() {
70     m_mutex.unlock();
71   }
72
73   void wait() {
74     m_cond.wait(&m_mutex);
75   }
76
77   void reset() {
78     m_done = false;
79   }
80
81   bool isDone() {
82     return m_done;
83   }
84
85 private:
86   void wake() {
87     lock();
88     m_done = true;
89     m_cond.wakeOne();
90     unlock();
91   }
92
93   bool m_done;
94   QMutex m_mutex;
95   QWaitCondition m_cond;
96 };
97
98 QtCamVideoMode::QtCamVideoMode(QtCamDevicePrivate *dev, QObject *parent) :
99   QtCamMode(new QtCamVideoModePrivate(dev), "mode-video", parent) {
100
101   d_ptr->init(new VideoDoneHandler(d_ptr, this));
102
103   d = (QtCamVideoModePrivate *)QtCamMode::d_ptr;
104
105   QString name = d_ptr->dev->conf->videoEncodingProfileName();
106   QString path = d_ptr->dev->conf->videoEncodingProfilePath();
107
108   if (!name.isEmpty() && !path.isEmpty()) {
109     GstEncodingProfile *profile = d_ptr->loadProfile(path, name);
110     if (profile) {
111       setProfile(profile);
112     }
113   }
114
115   QObject::connect(d_ptr->dev->q_ptr, SIGNAL(idleStateChanged(bool)),
116                    this, SLOT(_d_idleStateChanged(bool)));
117 }
118
119 QtCamVideoMode::~QtCamVideoMode() {
120   d = 0;
121 }
122
123 bool QtCamVideoMode::canCapture() {
124   return QtCamMode::canCapture() && d_ptr->dev->q_ptr->isIdle();
125 }
126
127 void QtCamVideoMode::applySettings() {
128   bool night = d_ptr->inNightMode();
129
130   int fps = night ? d->resolution.nightFrameRate() : d->resolution.frameRate();
131
132   d_ptr->setCaps("viewfinder-caps", d->resolution.captureResolution(), fps);
133
134   d_ptr->setCaps("video-capture-caps", d->resolution.captureResolution(), fps);
135
136   d_ptr->setPreviewSize(d->resolution.previewResolution());
137
138   // Not sure this is needed but just in case.
139   d_ptr->resetCaps("image-capture-caps");
140 }
141
142 void QtCamVideoMode::start() {
143   d_ptr->disableViewfinderFilters();
144 }
145
146 void QtCamVideoMode::stop() {
147   if (isRecording()) {
148     stopRecording(true);
149   }
150 }
151
152 bool QtCamVideoMode::isRecording() {
153   return !d_ptr->dev->q_ptr->isIdle();
154 }
155
156 bool QtCamVideoMode::startRecording(const QString& fileName, const QString& tmpFileName) {
157   if (!canCapture() || isRecording()) {
158     return false;
159   }
160
161   if (fileName.isEmpty()) {
162     return false;
163   }
164
165   d_ptr->setFileName(fileName);
166   d_ptr->setTempFileName(tmpFileName);
167
168   QString file = tmpFileName.isEmpty() ? fileName : tmpFileName;
169
170   QMetaObject::invokeMethod(d_ptr->dev->notifications, "videoRecordingStarted");
171
172   g_object_set(d_ptr->dev->cameraBin, "location", file.toUtf8().data(), NULL);
173   g_signal_emit_by_name(d_ptr->dev->cameraBin, "start-capture", NULL);
174
175   VideoDoneHandler *handler = dynamic_cast<VideoDoneHandler *>(d_ptr->doneHandler);
176   handler->reset();
177
178   emit recordingStateChanged();
179
180   emit canCaptureChanged();
181
182   return true;
183 }
184
185 void QtCamVideoMode::stopRecording(bool sync) {
186   if (isRecording()) {
187     VideoDoneHandler *handler = dynamic_cast<VideoDoneHandler *>(d_ptr->doneHandler);
188     if (sync) {
189       handler->lock();
190
191       if (handler->isDone()) {
192         handler->unlock();
193         return;
194       }
195     }
196
197     g_signal_emit_by_name(d_ptr->dev->cameraBin, "stop-capture", NULL);
198
199     if (sync) {
200       handler->wait();
201       handler->unlock();
202     }
203   }
204 }
205
206 bool QtCamVideoMode::setResolution(const QtCamVideoResolution& resolution) {
207   d->resolution = resolution;
208
209   if (!d_ptr->dev->q_ptr->isRunning()) {
210     // We will return true here because setting the resolution on a non-running pipeline
211     // doesn't make much sense (Probably the only use case is as a kind of optimization only).
212     // We will set it anyway when the pipeline gets started.
213     return true;
214   }
215
216   if (isRecording()) {
217     return false;
218   }
219
220   applySettings();
221
222   return true;
223 }
224
225 void QtCamVideoMode::setProfile(GstEncodingProfile *profile) {
226   if (!d_ptr->dev->cameraBin) {
227     gst_encoding_profile_unref(profile);
228     return;
229   }
230
231   g_object_set(d_ptr->dev->cameraBin, "video-profile", profile, NULL);
232 }
233
234 QtCamVideoSettings *QtCamVideoMode::settings() {
235   return d->settings;
236 }
237
238 #include "moc_qtcamvideomode.cpp"