Initial commit. corresponds to 1.0-1 release
[flashstrobe] / src / camera.cpp
1 /*
2   Copyright (C) 2010 by Juan Carlos Torres <jucato@kdemail.net>
3
4   This program is free software; you can redistribute it and/or
5   modify it under the terms of the GNU General Public License as
6   published by the Free Software Foundation; either version 2 of
7   the License or (at your option) version 3 or any later version
8   accepted by the membership of KDE e.V. (or its successor appro-
9   ved by the membership of KDE e.V.), which shall act as a proxy
10   defined in Section 14 of version 3 of the license.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program. If not, see http://www.gnu.org/licenses/.
19 */
20
21
22 #include "camera.h"
23
24 #include <QDBusConnection>
25 #include <QDBusMetaType>
26 #include <QDBusInterface>
27 #include <QDebug>
28
29 #include <stdlib.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32
33 #include <sys/mman.h>
34 #include <sys/ioctl.h>
35 #include <linux/videodev2.h>
36
37 /**
38  * This section sets up the necessary types and methods
39  * to create a D-Bus connection using Qt.
40  */
41
42 #define HAL_CAM_SHUTTER_UDI     "/org/freedesktop/Hal/devices/platform_cam_shutter"
43 #define HAL_CAM_SHUTTER_STATE   "button.state.value"
44
45 Q_DECLARE_METATYPE(Property)
46 Q_DECLARE_METATYPE(QList<Property>)
47
48 const QDBusArgument & operator<<(QDBusArgument &arg, const Property &change)
49 {
50     arg.beginStructure();
51     arg << change.name << change.added << change.removed;
52     arg.endStructure();
53     return arg;
54 }
55 const QDBusArgument & operator>>(const QDBusArgument &arg, Property &change)
56 {
57     arg.beginStructure();
58     arg >> change.name >> change.added >> change.removed;
59     arg.endStructure();
60     return arg;
61 }
62
63
64 /**
65  * Camera class definition
66  */
67
68 int Camera::m_fd = 0;
69 char Camera::m_deviceName[15] = "";
70
71 Camera::Camera(QObject* parent) : QObject(parent)
72 {
73     m_buffers = NULL;
74     m_numBuffers = 0;
75 }
76
77 Camera::~Camera()
78 {
79     if (m_fd != -1)
80     {
81         close();
82     }
83 }
84
85 /**
86  * Open and acquire a file descriptor for a file connected to
87  * the character device representing the camera, usually /dev/video0
88  */
89 int Camera::open(char* device)
90 {
91     struct stat st;
92
93     if (device == NULL)
94     {
95         qDebug() << "Device name not specified";
96         return Camera::GenericError;
97     }
98
99     memcpy(m_deviceName, device, sizeof(m_deviceName));
100
101     if (stat(m_deviceName, &st) == -1)
102     {
103         qDebug() << "Cannot identify device:" <<  m_deviceName;
104         return Camera::GenericError;
105     }
106
107     if (!S_ISCHR(st.st_mode))
108     {
109         qDebug() << "No such device: " << m_deviceName;
110         return Camera::GenericError;
111     }
112
113     m_fd = ::open(m_deviceName, O_RDWR | O_NONBLOCK, 0);
114
115     if (m_fd == -1)
116     {
117         qDebug() << "Cannot open device: " <<  m_deviceName;
118         return Camera::GenericError;
119     }
120
121     return m_fd;
122 }
123
124 int Camera::close()
125 {
126     if (m_fd != -1)
127     {
128         if (::close(m_fd) == -1)
129         {
130             qDebug() << "Cannot close device: " << m_deviceName;
131             return Camera::GenericError;
132         }
133     }
134
135     m_fd = -1;
136
137     return Camera::NoError;
138 }
139
140 /**
141  * Initializes the camera by setting up the
142  * cropping rectangle, data format and memory buffers
143  *
144  * Based on @ref http://v4l2spec.bytesex.org/spec/capture-example.html
145  */
146 int Camera::init()
147 {
148     struct v4l2_cropcap cropcap;
149     struct v4l2_crop crop;
150     struct v4l2_format fmt;
151     struct v4l2_requestbuffers req;
152
153     // Set cropping rectangle
154     cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
155     if (ioctl(m_fd, VIDIOC_CROPCAP, &cropcap) != -1)
156     {
157         crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
158         crop.c = cropcap.defrect; // reset to default
159
160         if (ioctl(m_fd, VIDIOC_S_CROP, &crop) == -1)
161         {
162             qDebug() << "Unable to set cropping rectangle";
163             return Camera::GenericError;
164         }
165     }
166     else
167     {
168         qDebug() << "Unable to get crop capabilities";
169         return Camera::GenericError;
170     }
171
172     // Set data format
173     fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
174     fmt.fmt.pix.width = 640;
175     fmt.fmt.pix.height = 480;
176     fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
177     fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
178
179     if (ioctl(m_fd, VIDIOC_S_FMT, &fmt) == -1)
180     {
181         qDebug() << "Unable to set data format";
182         return Camera::GenericError;
183     }
184
185     // Set up memory mapped buffers
186     req.count = 4;
187     req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
188     req.memory = V4L2_MEMORY_MMAP;
189
190     if (ioctl(m_fd, VIDIOC_REQBUFS, &req) == -1)
191     {
192         qDebug() << "No memory mapping support";
193         return Camera::GenericError;
194     }
195
196     if (req.count < 2)
197     {
198         qDebug() << "Insufficient buffer memory on device: " << m_deviceName;
199         return Camera::GenericError;
200     }
201
202     m_buffers = static_cast<buffer*>(calloc(req.count, sizeof(*m_buffers)));
203
204     if (!m_buffers)
205     {
206         qDebug() << "Out of memory";
207         return Camera::GenericError;
208     }
209
210     for (m_numBuffers = 0; m_numBuffers < req.count; ++m_numBuffers)
211     {
212         struct v4l2_buffer buf;
213
214         buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
215         buf.memory = V4L2_MEMORY_MMAP;
216         buf.index = m_numBuffers;
217
218         if (ioctl(m_fd, VIDIOC_QUERYBUF, &buf) == -1)
219         {
220             qDebug() << "Unable to query the status of buffer number: " <<  m_numBuffers;
221             return Camera::GenericError;
222         }
223
224         m_buffers[m_numBuffers].length = buf.length;
225         m_buffers[m_numBuffers].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE,
226                                              MAP_SHARED, m_fd, buf.m.offset);
227
228         if (m_buffers[m_numBuffers].start == MAP_FAILED)
229         {
230             qDebug() << "Unable to map memmory";
231             return Camera::GenericError;
232         }
233     }
234
235     return Camera::NoError;
236 }
237
238 int Camera::deinit()
239 {
240     quint32 i;
241
242     // Unmap memory mapped buffers
243     if (m_numBuffers > 0)
244     {
245         for (i = 0; i < m_numBuffers; ++i)
246         {
247             if (munmap(m_buffers[i].start, m_buffers[i].length) == -1)
248             {
249                 qDebug() << "Unable to unmap memory";
250                 return Camera::GenericError;
251             }
252         }
253
254         m_numBuffers = 0;
255     }
256
257     // Free allocated memory
258     if (m_buffers)
259     {
260         free (m_buffers);
261     }
262
263     m_buffers = NULL;
264
265     return Camera::NoError;
266 }
267
268 int Camera::stream(bool stream)
269 {
270     enum v4l2_buf_type type;
271
272     type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
273
274     // Start or stop streaming
275     if (stream)
276     {
277         if (ioctl(m_fd, VIDIOC_STREAMON, &type) == -1)
278         {
279             qDebug() << "Camera::stream: Unable to start streaming";
280             return Camera::GenericError;
281         }
282     }
283     else
284     {
285         if (ioctl(m_fd, VIDIOC_STREAMOFF, &type) == -1)
286         {
287             qDebug() << "Camera::stream: Unable to stop streaming";
288             return Camera::GenericError;
289         }
290     }
291
292     return Camera::NoError;
293 }
294
295 /**
296  * Strobes the camera flash ONCE only
297  */
298 int Camera::strobe()
299 {
300     struct v4l2_control ctrl;
301
302     ctrl.id = V4L2_CID_FLASH_STROBE;
303
304     if (ioctl(m_fd, VIDIOC_S_CTRL, &ctrl) == -1)
305     {
306         qDebug() << "Cannot set flash";
307         return Camera::GenericError;
308     }
309
310     return Camera::NoError;
311 }
312
313 /**
314  * Sets the LED intensity for strobe (flash) mode only.
315  * 
316  * Formulas:
317  *      code = (intensity - 35)/15
318  *      intensity = (code * 15) + 35
319  *
320  * Range: 215mA to 500mA
321  * 
322  * @param intensity Intensity in mA
323  */
324 int Camera::setFlashIntensity(int intensity)
325 {
326     struct v4l2_control ctrl;
327     int intensityCode = (intensity - 35)/15;
328
329     // Set intensity to safe limits
330     if (intensityCode > 31)
331     {
332         intensityCode = 31;
333     }
334     else if (intensityCode < 0)
335     {
336         intensityCode = 0;
337     }
338
339     // Set flash intensity
340     ctrl.id = V4L2_CID_FLASH_INTENSITY;
341     ctrl.value = intensityCode;
342     if (ioctl(m_fd, VIDIOC_S_CTRL, &ctrl) == -1)
343     {
344         qDebug() << "Cannot set intensity";
345         return Camera::GenericError;
346     }
347
348     if (intensity > 0)
349     {
350         stream(true);
351     }
352     else
353     {
354         stream(false);
355     }
356
357     return Camera::NoError;
358 }
359
360 int Camera::flashIntensity() const
361 {
362     struct v4l2_control ctrl;
363
364     ctrl.id = V4L2_CID_FLASH_INTENSITY;
365
366     if (ioctl(m_fd, VIDIOC_G_CTRL, &ctrl) == -1) {
367             qDebug() << "Cannot get intensity";
368             return Camera::GenericError;
369     }
370
371     return 35 + (ctrl.value * 15);
372 }
373
374 /**
375  * Sets the flash timeout. Works only in strobe (flash) mode.
376  * Formulas:
377  *      1 sec = 1,000 ms = 1,000,000 us (microseconds)
378  *
379  * Range: 54,600us to 820,000us (54.6ms to 820ms)
380  *
381  * @param timeout Timeout in microseconds
382  */
383 int Camera::setTimeout(quint32 timeout)
384 {
385     struct v4l2_control ctrl;
386
387     // Set timeout to safe limits
388     if (timeout < 54600)
389     {
390         timeout = 54600;
391     }
392     else if (timeout > 820000)
393     {
394         timeout = 820000;
395     }
396
397     // Set flash timeout
398     ctrl.id = V4L2_CID_FLASH_TIMEOUT;
399     ctrl.value = timeout;
400
401     if (ioctl(m_fd, VIDIOC_S_CTRL, &ctrl) == -1)
402     {
403         qDebug() << "Cannot set timeout";
404         return Camera::GenericError;
405     }
406
407     return Camera::NoError;
408 }
409
410 quint32 Camera::timeout() const
411 {
412     struct v4l2_control ctrl;
413
414     ctrl.id = V4L2_CID_FLASH_TIMEOUT;
415
416     if (ioctl(m_fd, VIDIOC_G_CTRL, &ctrl) == -1) {
417             qDebug() << "Cannot get timeout";
418             return Camera::GenericError;
419     }
420
421     return ctrl.value;
422 }
423
424 /**
425  * Sets up a D-Bus connection to listen for camera shutter
426  * state changes and calls the slot @ref shutterPropertyModified()
427  */
428 void Camera::registerDbusWatcher()
429 {
430     qDBusRegisterMetaType< Property >();
431     qDBusRegisterMetaType< QList<Property> >();
432
433     QDBusConnection::systemBus().connect(
434                     QString(),
435                     HAL_CAM_SHUTTER_UDI,
436                     "org.freedesktop.Hal.Device",
437                     "PropertyModified",
438                     this,
439                     SLOT(shutterPropertyModified(int, QList<Property>)));
440 }
441
442 /**
443  * Checks for the status of the camera shutter using D-Bus and HAL
444  */
445 bool Camera::isShutterOpen() const
446 {
447     QDBusInterface propertyInterface("org.freedesktop.Hal",
448                                      HAL_CAM_SHUTTER_UDI,
449                                      "org.freedesktop.Hal.Device",
450                                      QDBusConnection::systemBus());
451
452     bool isOpen = propertyInterface.call("GetProperty",
453                                          HAL_CAM_SHUTTER_STATE).arguments().at(0).toBool();
454
455     return !isOpen;
456 }
457
458 /**
459  * Called when camera shutter state changes. Checks for the
460  * actual state using @ref isShutterOpen() and emits the
461  * @ref shutterStateChanged() signal
462  */
463 void Camera::shutterPropertyModified(int /*numUpdates*/, QList< Property > /*updates*/)
464 {
465     bool isOpen = isShutterOpen();
466
467     emit shutterStateChanged(isOpen);
468 }