Diffstat (limited to 'noncore/multimedia/camera2/videocaptureview.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r-- | noncore/multimedia/camera2/videocaptureview.cpp | 599 |
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 | |||
62 | class 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 | |||
101 | bool VideoCapture::hasCamera() const | ||
102 | { | ||
103 | return (fd != -1); | ||
104 | } | ||
105 | |||
106 | QSize VideoCapture::captureSize() const | ||
107 | { | ||
108 | return QSize(width, height); | ||
109 | } | ||
110 | |||
111 | uint VideoCapture::refocusDelay() const | ||
112 | { | ||
113 | return 250; | ||
114 | } | ||
115 | |||
116 | int VideoCapture::minimumFramePeriod() const | ||
117 | { | ||
118 | return 40; // milliseconds | ||
119 | } | ||
120 | |||
121 | VideoCapture::VideoCapture() | ||
122 | { | ||
123 | setupCamera(QSize(0, 0)); | ||
124 | } | ||
125 | |||
126 | VideoCapture::~VideoCapture() | ||
127 | { | ||
128 | shutdown(); | ||
129 | } | ||
130 | |||
131 | void 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 | |||
263 | void 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 | |||
278 | void 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 | |||
322 | QValueList < 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 | |||
331 | QValueList < QSize > VideoCapture::videoSizes() const | ||
332 | { | ||
333 | // We use the same sizes for both. | ||
334 | return photoSizes(); | ||
335 | } | ||
336 | |||
337 | QSize VideoCapture::recommendedPhotoSize() const | ||
338 | { | ||
339 | return QSize(caps.maxwidth, caps.maxheight); | ||
340 | } | ||
341 | |||
342 | QSize VideoCapture::recommendedVideoSize() const | ||
343 | { | ||
344 | return QSize(caps.minwidth, caps.minheight); | ||
345 | } | ||
346 | |||
347 | QSize VideoCapture::recommendedPreviewSize() const | ||
348 | { | ||
349 | return QSize(caps.minwidth, caps.minheight); | ||
350 | } | ||
351 | |||
352 | void 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 | |||
364 | VideoCapture::VideoCapture() | ||
365 | { | ||
366 | } | ||
367 | |||
368 | VideoCapture::~VideoCapture() | ||
369 | { | ||
370 | } | ||
371 | |||
372 | bool VideoCapture::hasCamera() const | ||
373 | { | ||
374 | return TRUE; | ||
375 | } | ||
376 | |||
377 | QSize VideoCapture::captureSize() const | ||
378 | { | ||
379 | return QSize(640, 480); | ||
380 | } | ||
381 | |||
382 | uint VideoCapture::refocusDelay() const | ||
383 | { | ||
384 | return 0; | ||
385 | } | ||
386 | |||
387 | int VideoCapture::minimumFramePeriod() const | ||
388 | { | ||
389 | return 100; | ||
390 | } | ||
391 | |||
392 | static 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 | |||
409 | void 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 | |||
435 | QValueList < 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 | |||
443 | QValueList < 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 | |||
451 | QSize VideoCapture::recommendedPhotoSize() const | ||
452 | { | ||
453 | return QSize(640, 480); | ||
454 | } | ||
455 | |||
456 | QSize VideoCapture::recommendedVideoSize() const | ||
457 | { | ||
458 | return QSize(320, 240); | ||
459 | } | ||
460 | |||
461 | QSize VideoCapture::recommendedPreviewSize() const | ||
462 | { | ||
463 | return QSize(320, 240); | ||
464 | } | ||
465 | |||
466 | void VideoCapture::setCaptureSize(QSize size) | ||
467 | { | ||
468 | } | ||
469 | |||
470 | #endif /* !HAVE_VIDEO4LINUX */ | ||
471 | |||
472 | VideoCaptureView::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 | |||
482 | VideoCaptureView::~VideoCaptureView() | ||
483 | { | ||
484 | delete capture; | ||
485 | } | ||
486 | |||
487 | void 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 | |||
499 | void VideoCaptureView::setStill(const QImage & i) | ||
500 | { | ||
501 | setLive(-1); | ||
502 | img = i; | ||
503 | repaint(TRUE); | ||
504 | } | ||
505 | |||
506 | QValueList < QSize > VideoCaptureView::photoSizes() const | ||
507 | { | ||
508 | return capture->photoSizes(); | ||
509 | } | ||
510 | |||
511 | QValueList < QSize > VideoCaptureView::videoSizes() const | ||
512 | { | ||
513 | return capture->videoSizes(); | ||
514 | } | ||
515 | |||
516 | QSize VideoCaptureView::recommendedPhotoSize() const | ||
517 | { | ||
518 | return capture->recommendedPhotoSize(); | ||
519 | } | ||
520 | |||
521 | QSize VideoCaptureView::recommendedVideoSize() const | ||
522 | { | ||
523 | return capture->recommendedVideoSize(); | ||
524 | } | ||
525 | |||
526 | QSize VideoCaptureView::recommendedPreviewSize() const | ||
527 | { | ||
528 | return capture->recommendedPreviewSize(); | ||
529 | } | ||
530 | |||
531 | QSize VideoCaptureView::captureSize() const | ||
532 | { | ||
533 | return capture->captureSize(); | ||
534 | } | ||
535 | |||
536 | void VideoCaptureView::setCaptureSize(QSize size) | ||
537 | { | ||
538 | capture->setCaptureSize(size); | ||
539 | } | ||
540 | |||
541 | uint VideoCaptureView::refocusDelay() const | ||
542 | { | ||
543 | return capture->refocusDelay(); | ||
544 | } | ||
545 | |||
546 | bool VideoCaptureView::available() const | ||
547 | { | ||
548 | return capture->hasCamera(); | ||
549 | } | ||
550 | |||
551 | void 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 | |||
595 | void VideoCaptureView::timerEvent(QTimerEvent *) | ||
596 | { | ||
597 | repaint(FALSE); | ||
598 | } | ||
599 | |||