2
* V4L2 for Scratch (Derek O'Connell, 2009)
4
* This code can be used and distributed without restrictions.
15
#include <getopt.h> /* getopt_long() */
17
#include <fcntl.h> /* low-level i/o */
22
#include <sys/types.h>
25
#include <sys/ioctl.h>
28
#include <asm/types.h> /* for videodev2.h */
30
#include <linux/videodev2.h>
32
/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */
40
void *hLibv4l2 = NULL;
42
int (*vd_open)(const char *, int, ...);
45
int (*vd_ioctl)(int, unsigned long int, ...);
46
ssize_t (*vd_read)(int, void *, size_t);
47
void * (*vd_mmap)(void *, size_t, int, int, int, int64_t);
48
int (*vd_munmap)(void *, size_t);
50
/* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
53
#define CLEAR(x) memset (&(x), 0, sizeof (x))
70
int bmWidth, bmHeight;
74
struct buffer * buffers;
75
unsigned int nBuffers;
77
struct v4l2_buffer read_buf;
86
typedef struct camInfo_t *camPtr;
88
static char * videoDevName0 = "/dev/video0";
90
/* ================================== FUNCTION PROTOTYPES */
92
/* LIBRARY CONSTRUCTOR/DESCTRUCTOR */
94
void __attribute__ ((constructor)) libCon(void);
95
void __attribute__ ((destructor)) libDes(void);
100
inline int camIsOpen( camPtr cam) { return (-1 != cam->fileDesc); }
101
inline int camIsClosed(camPtr cam) { return (-1 == cam->fileDesc); }
107
/* SQUEAK INTERFACE */
109
sqInt CameraGetParam(int camNum, int paramNum);
110
sqInt CameraGetFrame(int camNum, unsigned char* buf, int pixelCount);
111
sqInt CameraExtent(int camNum);
112
char* CameraName(int camNum);
113
void CameraClose(int camNum);
114
sqInt CameraOpen(int camNum, int frameWidth, int frameHeight);
117
/* ================================== ??? */
120
/* LIBRARY CONSTRUCTOR/DESCTRUCTOR */
122
void __attribute__ ((constructor))
136
/* printf("libv4l2: use if available...");
138
hLibv4l2 = dlopen("libv4l2.so", RTLD_LAZY);
143
vd_open = dlsym(hLibv4l2, "v4l2_open");
144
vd_close = dlsym(hLibv4l2, "v4l2_close");
145
vd_dup = dlsym(hLibv4l2, "v4l2_dup");
146
vd_ioctl = dlsym(hLibv4l2, "v4l2_ioctl");
147
vd_read = dlsym(hLibv4l2, "v4l2_read");
148
vd_mmap = dlsym(hLibv4l2, "v4l2_mmap");
149
vd_munmap = dlsym(hLibv4l2, "v4l2_munmap");
151
/* printf("nay, %s\n", dlerror());
155
for (devNum = 0; devNum < 10; ++devNum) {
156
cam = &camInfo[devNum];
160
cam->devNum = devNum;
162
cam->ioMethod = IO_METHOD_MMAP;
163
cam->read_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
164
cam->read_buf.memory = V4L2_MEMORY_MMAP;
167
/* Pixel format now auto selected according to ease/speed of conversion
168
cam->pixelformat = V4L2_PIX_FMT_YUYV;
169
cam->pixelformat = V4L2_PIX_FMT_RGB24;
178
cam->read_buf = NULL;
180
cam->sqBufferBytes = 0;
187
void __attribute__ ((destructor))
191
for (camNum = 1; camNum < 11; ++camNum)
203
xioctl (camPtr cam, int request, void * arg)
207
do r = vd_ioctl (cam->fileDesc, request, arg);
208
while (-1 == r && EINTR == errno);
214
/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
215
from: palettes.c in VideoForLinuxPlugin
216
from: http://en.wikipedia.org/wiki/YUV422
218
Originally (here) a quick hack for XO-1 but libv4l
219
version worked anyway.
220
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */
222
inline unsigned char clipPixel(const int pixel) {
225
result = ((pixel < 0) ? 0 : pixel);
226
return (unsigned char) ((result > 255) ? 255: result);
230
convertPixelYUV444toARGB32(
231
const unsigned char y,
232
const unsigned char u,
233
const unsigned char v,
236
const int C = (y - 16) * 298 + 128;
237
const int D = u - 128;
238
const int E = v - 128;
241
dest[0] = clipPixel(( C + 516 * D ) >> 8);
242
dest[1] = clipPixel(( C - 100 * D - 208 * E) >> 8);
243
dest[2] = clipPixel(( C + 409 * E) >> 8);
249
convertImageYUYVToARGB32 (camPtr cam, int bufIdx)
253
const unsigned char* src = cam->buffers[bufIdx].start;
254
unsigned char* dst = cam->sqBuffer;
255
unsigned long int *pdst;
256
unsigned long int pixelCount = cam->sqPixels;
258
unsigned char u, y1, v, y2;
260
for (i = 0; i < pixelCount; i += 2) {
266
convertPixelYUV444toARGB32(y1, u, v, dst);
267
pdst = (unsigned long *)dst;
271
*(unsigned long *)dst = *pdst;
273
convertPixelYUV444toARGB32(y2, u, v, dst);
279
/* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
282
convertImageRGB24toARGB32 (camPtr cam, int bufIdx) /* const void *src, const void *dst) */
284
unsigned char *src = cam->buffers[bufIdx].start;
285
unsigned long int *dst = cam->sqBuffer;
286
unsigned long int pixelCount = cam->sqPixels;
287
unsigned long int pixel;
290
if (0 == dst) return;
292
for ( i = 0; i < pixelCount; i++) {
293
pixel = 0xFF000000 | (*src++ << 16);
294
pixel = pixel | (*src++ << 8);
295
*dst++ = pixel | *src++;
301
convertImageRGB444toARGB32 (camPtr cam, int bufIdx) /* const void *src, const void *dst) */
303
unsigned char *src = cam->buffers[bufIdx].start;
304
unsigned long int *dst = cam->sqBuffer;
305
unsigned long int pixelCount = cam->sqPixels;
306
unsigned long int r,g,b,pixel;
309
if (0 == dst) return;
311
/* Byte0: (g)ggg(b)bbb, Byte1: xxxx(r)rrr */
313
for ( i = 0; i < pixelCount; i++) {
316
b = (*src++ & 0x0F) << 4;
327
convertImageRGB565toARGB32 (camPtr cam, int bufIdx) /* const void *src, const void *dst) */
329
unsigned char *src = cam->buffers[bufIdx].start;
330
unsigned long int *dst = cam->sqBuffer;
331
unsigned long int pixelCount = cam->sqPixels;
332
unsigned long int r,g,b,pixel;
335
if (0 == dst) return;
337
/* Byte0: ggg(r)rrrr, Byte1: (b)bbbb(g)gg */
339
for ( i = 0; i < pixelCount; i++) {
340
r = (*src & 0x1F) << 3;
341
g = (*src++ & 0xE0) >> 5;
342
g |= (*src & 0x07) << 5;
354
convertImage (camPtr cam, int bufIdx)
356
/* func pts to be used at later date */
358
if (cam->pixelformat == V4L2_PIX_FMT_YUYV) {
359
convertImageYUYVToARGB32 (cam, bufIdx);
363
if (cam->pixelformat == V4L2_PIX_FMT_RGB565) {
364
convertImageRGB565toARGB32 (cam, bufIdx);
368
if (cam->pixelformat == V4L2_PIX_FMT_RGB444) {
369
convertImageRGB444toARGB32 (cam, bufIdx);
373
if (cam->pixelformat == V4L2_PIX_FMT_RGB24) {
374
convertImageRGB24toARGB32 (cam, bufIdx);
381
read_frame (camPtr cam)
383
struct v4l2_buffer buf;
385
cam->frameCount += 1;
388
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
389
buf.memory = V4L2_MEMORY_MMAP;
391
if (-1 == xioctl (cam, VIDIOC_DQBUF, &buf)) {
401
if (buf.index < cam->nBuffers)
402
convertImage (cam, buf.index);
404
if (-1 == xioctl (cam, VIDIOC_QBUF, &buf)) return -1;
413
int fd = cam->fileDesc;
421
while (retry-- > 0) {
429
r = select (fd + 1, &fds, NULL, NULL, &tv);
438
if (0 == r) return -1;
439
if (0 == read_frame (cam)) return 0;
447
stream_off (camPtr cam)
449
enum v4l2_buf_type type;
450
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
451
xioctl (cam, VIDIOC_STREAMOFF, &type);
457
stream_on (camPtr cam)
459
enum v4l2_buf_type type;
460
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
461
if (-1 == xioctl (cam, VIDIOC_STREAMON, &type)) return -1;
467
uninit_device (camPtr cam)
471
for (i = 0; i < cam->nBuffers; ++i)
472
if (-1 == vd_munmap (cam->buffers[i].start, cam->buffers[i].length))
481
queue_buffers (camPtr cam)
485
for (i = 0; i < cam->nBuffers; ++i) {
486
struct v4l2_buffer buf;
489
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
490
buf.memory = V4L2_MEMORY_MMAP;
493
if (-1 == xioctl (cam, VIDIOC_QBUF, &buf)) return -1;
501
init_mmap (camPtr cam)
503
struct v4l2_requestbuffers req;
507
req.count = cam->nBuffers;
508
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
509
req.memory = V4L2_MEMORY_MMAP;
511
if (-1 == xioctl (cam, VIDIOC_REQBUFS, &req)) {
512
if (EINVAL == errno) {
519
if (req.count < 1) return -1;
521
cam->buffers = calloc (req.count, sizeof (*(cam->buffers)));
522
if (!cam->buffers) return -1;
524
for (bufIdx = 0; bufIdx < req.count; ++bufIdx) {
525
struct v4l2_buffer buf;
528
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
529
buf.memory = V4L2_MEMORY_MMAP;
532
if (-1 == xioctl (cam, VIDIOC_QUERYBUF, &buf)) return -1;
534
cam->buffers[bufIdx].length = buf.length;
535
cam->buffers[bufIdx].start = vd_mmap (NULL /* start anywhere */,
537
PROT_READ | PROT_WRITE /* required */,
538
MAP_SHARED /* recommended */,
542
if (MAP_FAILED == cam->buffers[bufIdx].start) return -1;
549
set_format (camPtr cam, struct v4l2_format *fmt, int pixelformat, int w, int h)
552
fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
553
fmt->fmt.pix.field = V4L2_FIELD_TOP;
556
fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
557
fmt->fmt.pix.width = w;
558
fmt->fmt.pix.height = h;
559
fmt->fmt.pix.pixelformat = pixelformat;
560
fmt->fmt.pix.field = V4L2_FIELD_NONE; /* V4L2_FIELD_INTERLACED; */
561
if (-1 == xioctl (cam, VIDIOC_S_FMT, fmt)) return -1;
563
/* Note VIDIOC_S_FMT may change width and height. */
565
if ((w != fmt->fmt.pix.width) |
566
(h != fmt->fmt.pix.height) |
567
(fmt->fmt.pix.pixelformat != pixelformat))
572
cam->pixelformat = pixelformat;
578
init_device (camPtr cam, int w, int h)
580
struct v4l2_capability cap;
581
struct v4l2_cropcap cropcap;
582
struct v4l2_crop crop;
583
struct v4l2_format fmt;
587
if (-1 == xioctl (cam, VIDIOC_QUERYCAP, &cap)) {
588
if (EINVAL == errno) {
595
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) return -1;
596
if (!(cap.capabilities & V4L2_CAP_STREAMING)) return -1;
598
/* Select video input, video standard and tune here. */
601
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
603
if (0 == xioctl (cam, VIDIOC_CROPCAP, &cropcap)) {
604
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
605
crop.c = cropcap.defrect; /* reset to default */
607
if (-1 == xioctl (cam, VIDIOC_S_CROP, &crop)) {
608
if (EINVAL == errno) {
609
/* Cropping not supported (ignored) */
611
/* Errors ignored. */
615
/* Errors ignored. */
619
/* The order of preference of formats... */
620
if (-1 == set_format(cam, &fmt, V4L2_PIX_FMT_RGB24, w, h))
621
if (-1 == set_format(cam, &fmt, V4L2_PIX_FMT_YUYV, w, h))
622
if (-1 == set_format(cam, &fmt, V4L2_PIX_FMT_RGB565, w, h))
623
if (-1 == set_format(cam, &fmt, V4L2_PIX_FMT_RGB444, w, h))
627
V4L2_PIX_FMT_RGB24 : 3 bytes == 1 dst pixel
628
V4L2_PIX_FMT_RGB565: 2 bytes == 1 dst pixel
629
V4L2_PIX_FMT_RGB444: 2 bytes == 1 dst pixel
630
V4L2_PIX_FMT_YUYV : 4 bytes == 2 dst pixels
633
switch (fmt.fmt.pix.pixelformat) {
634
case V4L2_PIX_FMT_RGB24: /* printf("V4L2_PIX_FMT_RGB24\n"); */
637
case V4L2_PIX_FMT_RGB565: /* printf("V4L2_PIX_FMT_RGB565\n"); */
640
case V4L2_PIX_FMT_RGB444: /* printf("V4L2_PIX_FMT_RGB444\n"); */
643
case V4L2_PIX_FMT_YUYV: /* printf("V4L2_PIX_FMT_YUYV\n"); */
648
/* Buggy driver paranoia >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */
649
min = fmt.fmt.pix.width * bpp;
650
if (fmt.fmt.pix.bytesperline < min) fmt.fmt.pix.bytesperline = min;
651
min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
652
if (fmt.fmt.pix.sizeimage < min) fmt.fmt.pix.sizeimage = min;
653
/* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
655
if (0 > init_mmap(cam)) return -1;
656
if (0 > queue_buffers(cam)) return -1;
658
/* cache returned dims (make fmt a global?) */
659
cam->bmWidth = fmt.fmt.pix.width;
660
cam->bmHeight = fmt.fmt.pix.height;
661
cam->sqPixels = cam->bmWidth * cam->bmHeight;
662
cam->sqBufferBytes = cam->sqPixels * 4; /* Bytes to tx to Squeak (always RGB32) */
669
close_device (camPtr cam)
671
if (-1 == vd_close (cam->fileDesc)) return -1;
678
open_device (camPtr cam)
683
strcpy(deviceName, videoDevName0);
684
deviceName[10] = cam->devNum + '0';
686
if (-1 == stat (deviceName, &st)) return -1;
687
if (!S_ISCHR (st.st_mode)) return -1;
689
cam->fileDesc = vd_open (deviceName, O_RDWR /* required */ | O_NONBLOCK, 0);
691
if (camIsClosed(cam)) return -1;
698
InitCamera(camPtr cam, int w, int h)
700
cam->read_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
701
cam->read_buf.memory = V4L2_MEMORY_MMAP;
702
cam->ioMethod = IO_METHOD_MMAP;
704
if (0 > open_device(cam)) return -1;
706
if (0 > init_device(cam, w, h)) {
711
if (0 > stream_on(cam)) {
722
/* ============================================= SCRATCH I/F ==================================================== */
726
CameraGetParam(int camNum, int paramNum)
728
camPtr cam = &camInfo[camNum-1];
734
"Copy a camera frame into the given Bitmap. The Bitmap should be for a Form
735
of depth 32 that is the same width and height as the current camera frame.
736
Fail if the camera is not open or if the bitmap is not the right size. If
737
successful, answer the number of frames received from the camera since the
738
last call. If this is zero, then there has been no change."
743
CameraGetFrame(int camNum, unsigned char* buf, int pixelCount)
745
camPtr cam = &camInfo[camNum-1];
747
if (camIsClosed(cam)) return false;
748
if (pixelCount != cam->sqPixels) return false;
749
cam->sqBuffer = (void *)buf;
750
if (0 != getFrame(cam)) return 0;
756
CameraExtent(int camNum)
758
camPtr cam = &camInfo[camNum-1];
760
if (camIsClosed(cam)) return 0;
761
return (cam->bmWidth << 16) + cam->bmHeight;
766
CameraName(int camNum)
768
camPtr cam = &camInfo[camNum-1];
770
if (camIsClosed(cam)) return "camera not open";
771
return "default camera";
776
CameraClose(int camNum)
778
camPtr cam = &camInfo[camNum-1];
780
if (camIsClosed(cam)) return;
788
CameraOpen(int camNum, int frameWidth, int frameHeight)
790
camPtr cam = &camInfo[camNum-1];
792
if (camIsOpen(cam)) return false;
793
if (0 != InitCamera(cam, frameWidth, frameHeight)) return false;