summaryrefslogtreecommitdiff
path: root/noncore/multimedia/camera2/videocaptureview.cpp
Unidiff
Diffstat (limited to 'noncore/multimedia/camera2/videocaptureview.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--noncore/multimedia/camera2/videocaptureview.cpp599
1 files changed, 599 insertions, 0 deletions
diff --git a/noncore/multimedia/camera2/videocaptureview.cpp b/noncore/multimedia/camera2/videocaptureview.cpp
new file mode 100644
index 0000000..410634a
--- a/dev/null
+++ b/noncore/multimedia/camera2/videocaptureview.cpp
@@ -0,0 +1,599 @@
1/**********************************************************************
2** Copyright (C) 2000-2006 Trolltech AS. All rights reserved.
3**
4** This file is part of the Qtopia Environment.
5**
6** This program is free software; you can redistribute it and/or modify it
7** under the terms of the GNU General Public License as published by the
8** Free Software Foundation; either version 2 of the License, or (at your
9** option) any later version.
10**
11** A copy of the GNU GPL license version 2 is included in this package as
12** LICENSE.GPL.
13**
14** This program is distributed in the hope that it will be useful, but
15** WITHOUT ANY WARRANTY; without even the implied warranty of
16** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17** See the GNU General Public License for more details.
18**
19** In addition, as a special exception Trolltech gives permission to link
20** the code of this program with Qtopia applications copyrighted, developed
21** and distributed by Trolltech under the terms of the Qtopia Personal Use
22** License Agreement. You must comply with the GNU General Public License
23** in all respects for all of the code used other than the applications
24** licensed under the Qtopia Personal Use License Agreement. If you modify
25** this file, you may extend this exception to your version of the file,
26** but you are not obligated to do so. If you do not wish to do so, delete
27** this exception statement from your version.
28**
29** See http://www.trolltech.com/gpl/ for GPL licensing information.
30**
31** Contact info@trolltech.com if any conditions of this licensing are
32** not clear to you.
33**
34**********************************************************************/
35
36#include "videocaptureview.h"
37#include <qimage.h>
38#include <qpainter.h>
39#ifdef Q_WS_QWS
40#include <qgfx_qws.h>
41#include <qdirectpainter_qws.h>
42#endif
43
44#ifdef __linux__
45 #defineHAVE_VIDEO4LINUX 1
46#endif
47
48#ifdef HAVE_VIDEO4LINUX
49
50#include <sys/types.h>
51#include <sys/ioctl.h>
52#include <unistd.h>
53#include <fcntl.h>
54#include <linux/videodev.h>
55#include <errno.h>
56#include <string.h>
57#include <stdlib.h>
58#include <sys/mman.h>
59
60#endif /* HAVE_VIDEO4LINUX */
61
62class VideoCapture {
63 public:
64 VideoCapture();
65 ~VideoCapture();
66
67 bool hasCamera() const;
68 void getCameraImage(QImage & img, bool copy = FALSE);
69
70 QValueList < QSize > photoSizes() const;
71 QValueList < QSize > videoSizes() const;
72
73 QSize recommendedPhotoSize() const;
74 QSize recommendedVideoSize() const;
75 QSize recommendedPreviewSize() const;
76
77 QSize captureSize() const;
78 void setCaptureSize(QSize size);
79
80 uint refocusDelay() const;
81 int minimumFramePeriod() const;
82
83 private:
84#ifdef HAVE_VIDEO4LINUX
85 int fd;
86 int width, height;
87 struct video_capability caps;
88 struct video_mbuf mbuf;
89 unsigned char *frames;
90 int currentFrame;
91
92 void setupCamera(QSize size);
93 void shutdown();
94#endif
95};
96
97#ifdef HAVE_VIDEO4LINUX
98
99 #define VIDEO_DEVICE "/dev/video"
100
101bool VideoCapture::hasCamera() const
102{
103 return (fd != -1);
104}
105
106QSize VideoCapture::captureSize() const
107{
108 return QSize(width, height);
109}
110
111uint VideoCapture::refocusDelay() const
112{
113 return 250;
114}
115
116int VideoCapture::minimumFramePeriod() const
117{
118 return 40; // milliseconds
119}
120
121VideoCapture::VideoCapture()
122{
123 setupCamera(QSize(0, 0));
124}
125
126VideoCapture::~VideoCapture()
127{
128 shutdown();
129}
130
131void VideoCapture::setupCamera(QSize size)
132{
133 qWarning(" VideoCapture::setupCamera");
134 // Clear important variables.
135 frames = 0;
136 currentFrame = 0;
137 width = 640;
138 height = 480;
139 caps.minwidth = width;
140 caps.minheight = height;
141 caps.maxwidth = width;
142 caps.maxheight = height;
143
144 // Open the video device.
145 fd = open(VIDEO_DEVICE, O_RDWR);
146 if (fd == -1) {
147 qWarning("%s: %s", VIDEO_DEVICE, strerror(errno));
148 return;
149 }
150
151 // Get the device's current capabilities.
152 memset(&caps, 0, sizeof(caps));
153 if (ioctl(fd, VIDIOCGCAP, &caps) < 0) {
154 qWarning("%s: could not retrieve the video capabilities", VIDEO_DEVICE);
155 close(fd);
156 fd = -1;
157 return;
158 }
159
160 // Change the channel to the first-connected camera, skipping TV inputs.
161 // If there are multiple cameras, this may need to be modified.
162 int chan;
163 struct video_channel chanInfo;
164 qWarning("available video capture inputs:");
165 for (chan = 0; chan < caps.channels; ++chan) {
166 chanInfo.channel = chan;
167 if (ioctl(fd, VIDIOCGCHAN, &chanInfo) >= 0) {
168 if (chanInfo.type == VIDEO_TYPE_CAMERA)
169 qWarning(" %s (camera)", chanInfo.name);
170 else if (chanInfo.type == VIDEO_TYPE_TV)
171 qWarning(" %s (tv)", chanInfo.name);
172 else
173 qWarning(" %s (unknown)", chanInfo.name);
174 }
175 }
176 for (chan = 0; chan < caps.channels; ++chan) {
177 chanInfo.channel = chan;
178 if (ioctl(fd, VIDIOCGCHAN, &chanInfo) >= 0) {
179 if (chanInfo.type == VIDEO_TYPE_CAMERA) {
180 qWarning("selecting camera on input %s", chanInfo.name);
181 if (ioctl(fd, VIDIOCSCHAN, &chan) < 0) {
182 qWarning("%s: could not set the channel", VIDEO_DEVICE);
183 }
184 break;
185 }
186 }
187 }
188
189 // Set the desired picture mode to RGB32.
190 struct video_picture pict;
191 memset(&pict, 0, sizeof(pict));
192 ioctl(fd, VIDIOCGPICT, &pict);
193 pict.palette = VIDEO_PALETTE_RGB32;
194 if (ioctl(fd, VIDIOCSPICT, &pict) < 0) {
195 qWarning("%s: could not set the picture mode", VIDEO_DEVICE);
196 close(fd);
197 fd = -1;
198 return;
199 }
200
201 // Determine the capture size to use. Zero indicates "preview mode".
202 if (size.width() == 0) {
203 size = QSize(caps.minwidth, caps.minheight);
204 }
205
206 // Get the current capture window.
207 struct video_window wind;
208 memset(&wind, 0, sizeof(wind));
209 ioctl(fd, VIDIOCGWIN, &wind);
210
211 // Adjust the capture size to match the camera's aspect ratio.
212 if (caps.maxwidth > 0 && caps.maxheight > 0) {
213 if (size.width() > size.height()) {
214 size = QSize(size.height() * caps.maxwidth / caps.maxheight, size.height());
215 }
216 else {
217 size = QSize(size.width(), size.width() * caps.maxheight / caps.maxwidth);
218 }
219 }
220
221 // Set the new capture window.
222 wind.x = 0;
223 wind.y = 0;
224 wind.width = size.width();
225 wind.height = size.height();
226 if (ioctl(fd, VIDIOCSWIN, &wind) < 0) {
227 qWarning("%s: could not set the capture window", VIDEO_DEVICE);
228 }
229
230 // Re-read the capture window, to see what it was adjusted to.
231 ioctl(fd, VIDIOCGWIN, &wind);
232 width = wind.width;
233 height = wind.height;
234
235 // Enable mmap-based access to the camera.
236 memset(&mbuf, 0, sizeof(mbuf));
237 if (ioctl(fd, VIDIOCGMBUF, &mbuf) < 0) {
238 qWarning("%s: mmap-based camera access is not available", VIDEO_DEVICE);
239 close(fd);
240 fd = -1;
241 return;
242 }
243
244 // Mmap the designated memory region.
245 frames = (unsigned char *) mmap(0, mbuf.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
246 if (!frames || frames == (unsigned char *) (long) (-1)) {
247 qWarning("%s: could not mmap the device", VIDEO_DEVICE);
248 close(fd);
249 fd = -1;
250 return;
251 }
252
253 // Start capturing of the first frame.
254 struct video_mmap capture;
255 currentFrame = 0;
256 capture.frame = currentFrame;
257 capture.width = width;
258 capture.height = height;
259 capture.format = VIDEO_PALETTE_RGB32;
260 ioctl(fd, VIDIOCMCAPTURE, &capture);
261}
262
263void VideoCapture::shutdown()
264{
265 if (frames != 0) {
266 munmap(frames, mbuf.size);
267 frames = 0;
268 }
269 if (fd != -1) {
270 int flag = 0;
271 ioctl(fd, VIDIOCSYNC, 0);
272 ioctl(fd, VIDIOCCAPTURE, &flag);
273 close(fd);
274 fd = -1;
275 }
276}
277
278void VideoCapture::getCameraImage(QImage & img, bool copy)
279{
280 if (fd == -1) {
281 if (img.isNull()) {
282 img.create(width, height, 32);
283 }
284 return;
285 }
286
287 // Start capturing the next frame (we alternate between 0 and 1).
288 int frame = currentFrame;
289 struct video_mmap capture;
290 if (mbuf.frames > 1) {
291 currentFrame = !currentFrame;
292 capture.frame = currentFrame;
293 capture.width = width;
294 capture.height = height;
295 capture.format = VIDEO_PALETTE_RGB32;
296 ioctl(fd, VIDIOCMCAPTURE, &capture);
297 }
298
299 // Wait for the current frame to complete.
300 ioctl(fd, VIDIOCSYNC, &frame);
301
302 // Create an image that refers directly to the kernel's
303 // frame buffer, to avoid having to copy the data.
304 if (!copy) {
305 img = QImage(frames + mbuf.offsets[frame], width, height, 32, 0, 0, QImage::IgnoreEndian);
306 }
307 else {
308 img.create(width, height, 32);
309 memcpy(img.bits(), frames + mbuf.offsets[frame], width * height * 4);
310 }
311
312 // Queue up another frame if the device only supports one at a time.
313 if (mbuf.frames <= 1) {
314 capture.frame = currentFrame;
315 capture.width = width;
316 capture.height = height;
317 capture.format = VIDEO_PALETTE_RGB32;
318 ioctl(fd, VIDIOCMCAPTURE, &capture);
319 }
320}
321
322QValueList < QSize > VideoCapture::photoSizes() const
323{
324 QValueList < QSize > list;
325 list.append(QSize(caps.maxwidth, caps.maxheight));
326 if (caps.maxwidth != caps.minwidth || caps.maxheight != caps.minheight)
327 list.append(QSize(caps.minwidth, caps.minheight));
328 return list;
329}
330
331QValueList < QSize > VideoCapture::videoSizes() const
332{
333 // We use the same sizes for both.
334 return photoSizes();
335}
336
337QSize VideoCapture::recommendedPhotoSize() const
338{
339 return QSize(caps.maxwidth, caps.maxheight);
340}
341
342QSize VideoCapture::recommendedVideoSize() const
343{
344 return QSize(caps.minwidth, caps.minheight);
345}
346
347QSize VideoCapture::recommendedPreviewSize() const
348{
349 return QSize(caps.minwidth, caps.minheight);
350}
351
352void VideoCapture::setCaptureSize(QSize size)
353{
354 if (size.width() != width || size.height() != height) {
355 shutdown();
356 setupCamera(size);
357 }
358}
359
360#else /* !HAVE_VIDEO4LINUX */
361
362// Dummy implementation for systems without video.
363
364VideoCapture::VideoCapture()
365{
366}
367
368VideoCapture::~VideoCapture()
369{
370}
371
372bool VideoCapture::hasCamera() const
373{
374 return TRUE;
375}
376
377QSize VideoCapture::captureSize() const
378{
379 return QSize(640, 480);
380}
381
382uint VideoCapture::refocusDelay() const
383{
384 return 0;
385}
386
387int VideoCapture::minimumFramePeriod() const
388{
389 return 100;
390}
391
392static unsigned int nextrand()
393{
394#define A 16807
395#define M 2147483647
396#define Q 127773
397#define R 2836
398 static unsigned int rnd = 1;
399 unsigned long hi = rnd / Q;
400 unsigned long lo = rnd % Q;
401 unsigned long test = A * lo - R * hi;
402 if (test > 0)
403 rnd = test;
404 else
405 rnd = test + M;
406 return rnd;
407}
408
409void VideoCapture::getCameraImage(QImage & img, bool)
410{
411 // Just generate something dynamic (rectangles)
412 static QImage cimg;
413 int x, y, w, h;
414 if (cimg.isNull()) {
415 x = y = 0;
416 w = 640;
417 h = 480;
418 cimg.create(w, h, 32);
419 }
420 else {
421 w = nextrand() % (cimg.width() - 10) + 10;
422 h = nextrand() % (cimg.height() - 10) + 10;
423 x = nextrand() % (cimg.width() - w);
424 y = nextrand() % (cimg.height() - h);
425 }
426 QRgb c = qRgb(nextrand() % 255, nextrand() % 255, nextrand() % 255);
427 for (int j = 0; j < h; j++) {
428 QRgb *l = (QRgb *) cimg.scanLine(y + j) + x;
429 for (int i = 0; i < w; i++)
430 l[i] = c;
431 }
432 img = cimg;
433}
434
435QValueList < QSize > VideoCapture::photoSizes() constconst
436{
437 QValueList < QSize > list;
438 list.append(QSize(640, 480));
439 list.append(QSize(320, 240));
440 return list;
441}
442
443QValueList < QSize > VideoCapture::videoSizes() constconst
444{
445 QValueList < QSize > list;
446 list.append(QSize(640, 480));
447 list.append(QSize(320, 240));
448 return list;
449}
450
451QSize VideoCapture::recommendedPhotoSize() const
452{
453 return QSize(640, 480);
454}
455
456QSize VideoCapture::recommendedVideoSize() const
457{
458 return QSize(320, 240);
459}
460
461QSize VideoCapture::recommendedPreviewSize() const
462{
463 return QSize(320, 240);
464}
465
466void VideoCapture::setCaptureSize(QSize size)
467{
468}
469
470#endif /* !HAVE_VIDEO4LINUX */
471
472VideoCaptureView::VideoCaptureView(QWidget * parent, const char *name, WFlags fl):QWidget(parent,
473 name, fl)
474{
475 capture = new VideoCapture();
476 QSizePolicy sp(QSizePolicy::Expanding, QSizePolicy::Expanding);
477 setSizePolicy(sp);
478 tid_update = 0;
479 setLive();
480}
481
482VideoCaptureView::~VideoCaptureView()
483{
484 delete capture;
485}
486
487void VideoCaptureView::setLive(int period)
488{
489 if (tid_update)
490 killTimer(tid_update);
491 if (period == 0)
492 tid_update = startTimer(capture->minimumFramePeriod());
493 else if (period > 0)
494 tid_update = startTimer(period);
495 else
496 tid_update = 0;
497}
498
499void VideoCaptureView::setStill(const QImage & i)
500{
501 setLive(-1);
502 img = i;
503 repaint(TRUE);
504}
505
506QValueList < QSize > VideoCaptureView::photoSizes() const
507{
508 return capture->photoSizes();
509}
510
511QValueList < QSize > VideoCaptureView::videoSizes() const
512{
513 return capture->videoSizes();
514}
515
516QSize VideoCaptureView::recommendedPhotoSize() const
517{
518 return capture->recommendedPhotoSize();
519}
520
521QSize VideoCaptureView::recommendedVideoSize() const
522{
523 return capture->recommendedVideoSize();
524}
525
526QSize VideoCaptureView::recommendedPreviewSize() const
527{
528 return capture->recommendedPreviewSize();
529}
530
531QSize VideoCaptureView::captureSize() const
532{
533 return capture->captureSize();
534}
535
536void VideoCaptureView::setCaptureSize(QSize size)
537{
538 capture->setCaptureSize(size);
539}
540
541uint VideoCaptureView::refocusDelay() const
542{
543 return capture->refocusDelay();
544}
545
546bool VideoCaptureView::available() const
547{
548 return capture->hasCamera();
549}
550
551void VideoCaptureView::paintEvent(QPaintEvent *)
552{
553 if (tid_update && !capture->hasCamera()) {
554 QPainter p(this);
555 p.drawText(rect(), AlignCenter, tr("No Camera"));
556 return;
557 }
558
559 if (tid_update)
560 capture->getCameraImage(img);
561 int w = img.width();
562 int h = img.height();
563
564 if (!w || !h)
565 return;
566
567 if (width() * w > height() * h) {
568 w = w * height() / h;
569 h = height();
570 }
571 else {
572 h = h * width() / w;
573 w = width();
574 }
575
576 if (qt_screen->transformOrientation() == 0) {
577 // Stretch and draw the image.
578 QDirectPainter p(this);
579 QGfx *gfx = p.internalGfx();
580 if (gfx) {
581 gfx->setSource(&img);
582 gfx->setAlphaType(QGfx::IgnoreAlpha);
583 gfx->stretchBlt((width() - w) / 2, (height() - h) / 2, w, h, img.width(), img.height());
584 }
585 }
586 else {
587 // This code is nowhere near efficient enough (hence the above).
588 // TODO - handle rotations during direct painting.
589 QImage scimg = img.smoothScale(w, h);
590 QPainter p(this);
591 p.drawImage((width() - w) / 2, (height() - h) / 2, scimg);
592 }
593}
594
595void VideoCaptureView::timerEvent(QTimerEvent *)
596{
597 repaint(FALSE);
598}
599