• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2    Digital still camera.
3 
4    SDL based, suitable for camera phone such as Nokia N900. In
5    particular, we support focus, gain and exposure control, but not
6    aperture control or lens zoom.
7 
8    Copyright 2017 Pavel Machek, LGPL
9 
10    Needs sdl2, sdl2_image libraries. sudo aptitude install libsdl2-dev
11    libsdl2-image-dev on Debian systems.
12 */
13 
14 #include <time.h>
15 #include <errno.h>
16 #include <stdarg.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <sys/types.h>
22 #include <sys/mman.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <sys/time.h>
27 #include <sys/ioctl.h>
28 #include <fcntl.h>
29 
30 #include <jpeglib.h>
31 
32 #include "libv4l2.h"
33 #include <linux/videodev2.h>
34 #include "libv4l-plugin.h"
35 
36 #include <SDL2/SDL.h>
37 #include <SDL2/SDL_image.h>
38 
39 #define PICDIR "."
40 #define SX 2592
41 #define SY 1968
42 #define SIZE SX*SY*3
43 
fmt_print(struct v4l2_format * fmt)44 static void fmt_print(struct v4l2_format *fmt)
45 {
46 	int f;
47 	printf("Format: %dx%d. ", fmt->fmt.pix.width, fmt->fmt.pix.height);
48 	printf("%x ", fmt->fmt.pix.pixelformat);
49 	f = fmt->fmt.pix.pixelformat;
50 	for (int i=0; i<4; i++) {
51 		printf("%c", f & 0xff);
52 		f >>= 8;
53 	}
54 	printf("\n");
55 }
56 
dtime(void)57 static double dtime(void)
58 {
59 	static double start = 0.0;
60 	struct timeval now;
61 
62 	gettimeofday(&now, NULL);
63 
64 	double n = now.tv_sec + now.tv_usec / 1000000.;
65 	if (!start)
66 		start = n;
67 	return n - start;
68 }
69 
v4l2_g_ctrl(int fd,long id)70 static long v4l2_g_ctrl(int fd, long id)
71 {
72 	int res;
73 	struct v4l2_control ctrl;
74 	ctrl.id = id;
75 	ctrl.value = 0;
76 	res = v4l2_ioctl(fd, VIDIOC_G_CTRL, &ctrl);
77 	if (res < 0)
78 		printf("Get control %lx failed\n", id);
79 	return ctrl.value;
80 }
81 
v4l2_s_ctrl(int fd,long id,long value)82 static int v4l2_s_ctrl(int fd, long id, long value)
83 {
84 	int res;
85 	struct v4l2_control ctrl;
86 	ctrl.id = id;
87 	ctrl.value = value;
88 	res = v4l2_ioctl(fd, VIDIOC_S_CTRL, &ctrl);
89 	if (res < 0)
90 		printf("Set control %lx %ld failed\n", id, value);
91 	return res;
92 }
93 
v4l2_set_focus(int fd,int diopt)94 static int v4l2_set_focus(int fd, int diopt)
95 {
96 	if (v4l2_s_ctrl(fd, V4L2_CID_FOCUS_ABSOLUTE, diopt) < 0) {
97 		printf("Could not set focus\n");
98 	}
99 	return 0;
100 }
101 
102 struct dev_info {
103 	int fd;
104 	struct v4l2_format fmt;
105 
106 	unsigned char buf[SIZE];
107 	int debug;
108 #define D_TIMING 1
109 };
110 
111 struct sdl {
112 	SDL_Window *window;
113 	SDL_Surface *screen, *liveview;
114 
115 	int wx, wy; /* Window size */
116 	int sx, sy; /* Live view size */
117 	int bx, by; /* Border size */
118 	int nx, ny; /* Number of buttons */
119 	float factor;
120 
121 	/* These should go separately */
122 	int do_focus, do_exposure, do_flash, do_white, do_big, do_full;
123 	double zoom;
124 	double focus_min;
125 
126 	int slider_mode;
127 #define M_BIAS 0
128 #define M_EXPOSURE 1
129 #define M_GAIN 2
130 #define M_FOCUS 3
131 #define M_NUM 4
132 
133 	int fd;
134 
135 	struct dev_info *dev;
136 };
137 
138 typedef struct {
139 	uint8_t r, g, b, alpha;
140 } pixel;
141 
142 #define d_raw 1
143 
sfc_put_pixel(SDL_Surface * liveview,int x,int y,pixel * p)144 static void sfc_put_pixel(SDL_Surface* liveview, int x, int y, pixel *p)
145 {
146 	Uint32* p_liveview = (Uint32*)liveview->pixels;
147 	p_liveview += y*liveview->w+x;
148 	*p_liveview = SDL_MapRGBA(liveview->format, p->r, p->g, p->b, p->alpha);
149 }
150 
sdl_begin_paint(struct sdl * m)151 static void sdl_begin_paint(struct sdl *m)
152 {
153 	/* Fill the surface white */
154 	SDL_FillRect(m->liveview, NULL, SDL_MapRGB( m->liveview->format, 0, 0, 0 ));
155 
156 	SDL_LockSurface(m->liveview);
157 }
158 
sdl_finish_paint(struct sdl * m)159 static void sdl_finish_paint(struct sdl *m) {
160 	SDL_UnlockSurface(m->liveview);
161 	SDL_Rect rcDest = { m->bx, m->by, m->sx, m->sy };
162 
163 	SDL_BlitSurface(m->liveview, NULL, m->screen, &rcDest);
164 	SDL_UpdateWindowSurfaceRects(m->window, &rcDest, 1);
165 }
166 
sdl_paint_image(struct sdl * m,char ** xpm,int x,int y)167 static void sdl_paint_image(struct sdl *m, char **xpm, int x, int y) {
168 	SDL_Surface *image = IMG_ReadXPMFromArray(xpm);
169 	if (!image) {
170 		printf("IMG_Load: %s\n", IMG_GetError());
171 		exit(1);
172 	}
173 
174 	int x_pos = x - image->w/2, y_pos = y - image->h/2;
175 
176 	SDL_Rect rcDest = { x_pos, y_pos, image->w, image->h };
177 	int r = SDL_BlitSurface(image, NULL, m->screen, &rcDest);
178 
179 	if (r) {
180 		printf("Error blitting: %s\n", SDL_GetError());
181 		exit(1);
182 	}
183 	SDL_FreeSurface(image);
184 }
185 
cam_exposure_limits(struct sdl * m,struct v4l2_queryctrl * qctrl)186 static void cam_exposure_limits(struct sdl *m, struct v4l2_queryctrl *qctrl)
187 {
188 	qctrl->id = V4L2_CID_EXPOSURE_ABSOLUTE;
189 
190 	if (v4l2_ioctl(m->fd, VIDIOC_QUERYCTRL, qctrl)) {
191 		printf("Exposure absolute limits failed\n");
192 		exit(1);
193 	}
194 
195 	/* Minimum of 11300 gets approximately same range on ISO and
196 	 * exposure axis. */
197 	if (qctrl->minimum < 500)
198 		qctrl->minimum = 500;
199 }
200 
cam_set_exposure(struct sdl * m,double v)201 static void cam_set_exposure(struct sdl *m, double v)
202 {
203 	int cid = V4L2_CID_EXPOSURE_ABSOLUTE;
204 	double res;
205 	double range;
206 	struct v4l2_queryctrl qctrl = { .id = cid };
207 	struct v4l2_control ctrl = { .id = cid };
208 
209 	cam_exposure_limits(m, &qctrl);
210 
211 	if (v4l2_ioctl(m->fd, VIDIOC_G_CTRL, &ctrl)) {
212 		printf("Can't get exposure parameters\n");
213 		exit(1);
214 	}
215 
216 	range = log2(qctrl.maximum) - log2(qctrl.minimum);
217 	res = log2(qctrl.minimum) + v*range;
218 	res = exp2(res);
219 
220 	v4l2_s_ctrl(m->fd, V4L2_CID_EXPOSURE_ABSOLUTE, res);
221 }
222 
cam_convert_exposure(struct sdl * m,int v)223 static double cam_convert_exposure(struct sdl *m, int v)
224 {
225 	int cid = V4L2_CID_EXPOSURE_ABSOLUTE;
226 	double res;
227 	struct v4l2_queryctrl qctrl = { .id = cid };
228 
229 	cam_exposure_limits(m, &qctrl);
230 	res = (log2(v) - log2(qctrl.minimum)) / (log2(qctrl.maximum) - log2(qctrl.minimum));
231 
232 	return res;
233 }
234 
cam_get_exposure(struct sdl * m)235 static double cam_get_exposure(struct sdl *m)
236 {
237 	int cid = V4L2_CID_EXPOSURE_ABSOLUTE;
238 	struct v4l2_control ctrl = { .id = cid };
239 
240 	if (v4l2_ioctl(m->fd, VIDIOC_G_CTRL, &ctrl))
241 		return -1;
242 
243 	return cam_convert_exposure(m, ctrl.value);
244 }
245 
cam_get_gain_iso(struct dev_info * dev)246 static int cam_get_gain_iso(struct dev_info *dev)
247 {
248 	double x;
249 
250 	x = v4l2_g_ctrl(dev->fd, 0x00980913);
251 	x = (x / 10); /* EV */
252 	x = exp2(x);
253 	x = 100*x;
254 	return x;
255 }
256 
cam_set_focus(struct sdl * m,double val)257 static void cam_set_focus(struct sdl *m, double val)
258 {
259 	v4l2_s_ctrl(m->fd, V4L2_CID_FOCUS_ABSOLUTE, (val * m->focus_min) * (1023. / 20));
260 }
261 
cam_convert_focus(struct sdl * m,double diopter)262 static double cam_convert_focus(struct sdl *m, double diopter)
263 {
264 	return diopter / m->focus_min;
265 }
266 
cam_get_focus_diopter(struct sdl * m)267 static double cam_get_focus_diopter(struct sdl *m)
268 {
269 	return (v4l2_g_ctrl(m->fd, V4L2_CID_FOCUS_ABSOLUTE) * 20.) / 1023.;
270 }
271 
cam_get_focus(struct sdl * m)272 static double cam_get_focus(struct sdl *m)
273 {
274 	return cam_convert_focus(m, cam_get_focus_diopter(m));
275 }
276 
sdl_line_color(struct sdl * m,int x1,int y1,int x2,int y2,Uint32 color)277 static void sdl_line_color(struct sdl *m, int x1, int y1, int x2, int y2, Uint32 color)
278 {
279 	SDL_Rect rcDest = { x1, y1, x2-x1+1, y2-y1+1};
280 
281 	SDL_FillRect(m->screen, &rcDest, color);
282 }
283 
sdl_line(struct sdl * m,int x1,int y1,int x2,int y2)284 static void sdl_line(struct sdl *m, int x1, int y1, int x2, int y2)
285 {
286 	sdl_line_color(m, x1, y1, x2, y2, SDL_MapRGB( m->liveview->format, 255, 255, 255 ));
287 }
288 
sdl_digit(struct sdl * m,int x,int y,int s,int i)289 static void sdl_digit(struct sdl *m, int x, int y, int s, int i)
290 {
291 	unsigned char gr[] = { 0x5f, 0x0a, 0x76, 0x7a, 0x2b,
292 			       0x79, 0x7d, 0x1a, 0x7f, 0x7b };
293 	unsigned char g = gr[i];
294 	/*
295               10
296 	    01  02
297               20
298             04  08
299 	      40
300 	*/
301 
302 	if (g & 1) sdl_line(m, x, y, x, y+s);
303 	if (g & 2) sdl_line(m, x+s, y, x+s, y+s);
304 	if (g & 4) sdl_line(m, x, y+s, x, y+s+s);
305 	if (g & 8) sdl_line(m, x+s, y+s, x+s, y+s+s);
306 
307 	if (g & 0x10) sdl_line(m, x, y, x+s, y);
308 	if (g & 0x20) sdl_line(m, x, y+s, x+s, y+s);
309 	if (g & 0x40) sdl_line(m, x, y+s+s, x+s, y+s+s);
310 }
311 
sdl_number(struct sdl * m,int x,int y,int s,int digits,int i)312 static void sdl_number(struct sdl *m, int x, int y, int s, int digits, int i)
313 {
314 	int tot = s * 5;
315 	x += tot * digits;
316 	for (int j=0; j<digits; j++) {
317 		sdl_digit(m, x+2, y+4, s*3, i%10);
318 		i /= 10;
319 		x -= tot;
320 	}
321 }
322 
sdl_icon_pos(struct sdl * m,int * x,int * y,double val)323 static void sdl_icon_pos(struct sdl *m, int *x, int *y, double val)
324 {
325 	*x = m->wx - 10;
326 	*y = val * m->wy;
327 }
328 
sdl_paint_ui_iso(struct sdl * m,double y_,int i)329 static void sdl_paint_ui_iso(struct sdl *m, double y_, int i)
330 {
331 	static char *iso_xpm[] = {
332 		"16 12 2 1",
333 		"x c #ffffff",
334 		". c #000000",
335 		"................",
336 		"................",
337 		"................",
338 		".x..xx..x.......",
339 		".x.x...x.x......",
340 		".x..x..x.x......",
341 		".x...x.x.x......",
342 		".x.xx...x.......",
343 		"................",
344 		"................",
345 		"................",
346 		"................",
347 	};
348 
349 	int x, y;
350 
351 	sdl_icon_pos(m, &x, &y, y_);
352 	sdl_number(m, x-35, y-10, 1, 3, i);
353 	sdl_paint_image(m, iso_xpm, x, y);
354 }
355 
sdl_paint_ui_exposure(struct sdl * m,int t)356 static void sdl_paint_ui_exposure(struct sdl *m, int t)
357 {
358 	static char *time_1_xpm[] = {
359 		"16 12 2 1",
360 		"x c #ffffff",
361 		". c #000000",
362 		"......x.........",
363 		".....x..........",
364 		"....x...........",
365 		"...x............",
366 		"................",
367 		".xxx.xxxx.xxxx..",
368 		"x....x....x.....",
369 		".xx..xxx..x.....",
370 		"...x.x....x.....",
371 		"...x.x....x.....",
372 		"xxx..xxxx.xxxx..",
373 		"................",
374 	};
375 	int x, y;
376 
377 	sdl_icon_pos(m, &x, &y, cam_convert_exposure(m, 1000000/t));
378 	sdl_number(m, x-35, y-10, 1, 3, t);
379 	sdl_paint_image(m, time_1_xpm, x, y);
380 }
381 
sdl_paint_boolean(struct sdl * m,char ** image,int x,int y,int yes)382 static void sdl_paint_boolean(struct sdl *m, char **image, int x, int y, int yes)
383 {
384 	static char *not_xpm[] = {
385 		"16 12 2 1",
386 		"x c #ffffff",
387 		". c #000000",
388 		"......xxxxx.....",
389 		"....xx.....xx...",
390 		"...x.........x..",
391 		"..x........xx.x.",
392 		"..x......xx...x.",
393 		".x.....xx......x",
394 		".x...xx........x",
395 		"..xxx.........x.",
396 		"..x...........x.",
397 		"...x.........x..",
398 		"....xx.....xx...",
399 		"......xxxxx.....",
400 	};
401 
402 	sdl_paint_image(m, image, x, y);
403 	if (!yes)
404 		sdl_paint_image(m, not_xpm,  16+x, y);
405 }
406 
sdl_paint_button(struct sdl * m,char ** image,int x,int y,int yes)407 static void sdl_paint_button(struct sdl *m, char **image, int x, int y, int yes)
408 {
409 	int bsx = m->wx/m->nx;
410 	int bsy = m->wy/m->ny;
411 	if (x < 0)
412 		x += m->nx;
413 	if (y < 0)
414 		y += m->ny;
415 	x = bsx/2 + x*bsx;
416 	y = bsy/2 + y*bsy;
417 	sdl_paint_boolean(m, image, x, y, yes);
418 }
419 
sdl_paint_ui_focus(struct sdl * m,int f)420 static void sdl_paint_ui_focus(struct sdl *m, int f)
421 {
422 	static char *cm_xpm[] = {
423 		"16 9 2 1",
424 		"# c #ffffff",
425 		". c #000000",
426 		"................",
427 		"................",
428 		"................",
429 		"....###..#.#.##.",
430 		"...#.....##.#..#",
431 		"...#.....#..#..#",
432 		"...#.....#..#..#",
433 		"....###..#..#..#",
434 		"................",
435 	};
436 	double dioptr = 1/(f/100.);
437 	int x, y;
438 
439 	if (dioptr > m->focus_min)
440 		return;
441 
442 	sdl_icon_pos(m, &x, &y, cam_convert_focus(m, dioptr));
443 	sdl_paint_image(m, cm_xpm, x, y);
444 	sdl_number(m, x-12, y-15, 1, 3, f);
445 }
446 
sdl_paint_ui_bias(struct sdl * m,double v)447 static void sdl_paint_ui_bias(struct sdl *m, double v)
448 {
449 	static char *bias_xpm[] = {
450 		"16 12 2 1",
451 		"# c #ffffff",
452 		". c #000000",
453 		"...#............",
454 		"...#.......#....",
455 		".#####....#.....",
456 		"...#.....#......",
457 		"...#....#.......",
458 		".......#........",
459 		"......#...#####.",
460 		".....#..........",
461 		"....#...........",
462 		"...#............",
463 		"................",
464 		"................",
465 	};
466 	int x, y;
467 
468 	sdl_icon_pos(m, &x, &y, v);
469 	sdl_paint_image(m, bias_xpm, x, y);
470 }
471 
sdl_paint_slider(struct sdl * m)472 static void sdl_paint_slider(struct sdl *m)
473 {
474 	switch (m->slider_mode) {
475 	case M_BIAS:
476 		sdl_paint_ui_bias(m, 0.5);
477 		return;
478 	case M_EXPOSURE:
479 		sdl_paint_ui_exposure(m, 10);
480 		sdl_paint_ui_exposure(m, 100);
481 		sdl_paint_ui_exposure(m, 999);
482 		return;
483 	case M_GAIN:
484 		sdl_paint_ui_iso(m, 0/4., 100);
485 		sdl_paint_ui_iso(m, 1/4., 200);
486 		sdl_paint_ui_iso(m, 2/4., 400);
487 		sdl_paint_ui_iso(m, 3/4., 800);
488 		return;
489 	case M_FOCUS:
490 		sdl_paint_ui_focus(m, 100);
491 		sdl_paint_ui_focus(m, 40);
492 		sdl_paint_ui_focus(m, 25);
493 		sdl_paint_ui_focus(m, 16);
494 		sdl_paint_ui_focus(m, 10);
495 		sdl_paint_ui_focus(m, 8);
496 		sdl_paint_ui_focus(m, 6);
497 		return;
498 	}
499 }
500 
sdl_paint_ui(struct sdl * m)501 static void sdl_paint_ui(struct sdl *m)
502 {
503 	static char *wait_xpm[] = {
504 		"16 9 2 1",
505 		"# c #ffffff",
506 		". c #000000",
507 		"....########....",
508 		".....#....#.....",
509 		".....#....#.....",
510 		"......#..#......",
511 		".......##.......",
512 		"......#..#......",
513 		".....#....#.....",
514 		".....#....#.....",
515 		"....########....",
516 	};
517 
518 	static char *ok_xpm[] = {
519 		"16 9 2 1",
520 		"# c #ffffff",
521 		". c #000000",
522 		"...............#",
523 		"............###.",
524 		"..........##....",
525 		"#.......##......",
526 		".#.....#........",
527 		"..#...#.........",
528 		"..#..#..........",
529 		"...##...........",
530 		"...#............",
531 	};
532 
533 	static char *exit_xpm[] = {
534 		"16 9 2 1",
535 		"x c #ffffff",
536 		". c #000000",
537 		"....x......x....",
538 		".....x....x.....",
539 		"......x..x......",
540 		".......xx.......",
541 		".......xx.......",
542 		"......x..x......",
543 		".....x....x.....",
544 		"....x......x....",
545 		"................",
546 	};
547 
548 	static char *af_xpm[] = {
549 		"16 12 2 1",
550 		"x c #ffffff",
551 		". c #000000",
552 		"................",
553 		"................",
554 		".....xxxxxxx....",
555 		".....x..........",
556 		".....x..........",
557 		".x...xxxxx......",
558 		"x.x..x..........",
559 		"xxx..x..........",
560 		"x.x..x..........",
561 		"x.x..x..........",
562 		"................",
563 		"................",
564 	};
565 
566 	static char *ae_xpm[] = {
567 		"16 12 2 1",
568 		"x c #ffffff",
569 		". c #000000",
570 		"................",
571 		"................",
572 		".....xxxxxxx....",
573 		".....x..........",
574 		".....x..........",
575 		".x...xxxxx......",
576 		"x.x..x..........",
577 		"xxx..x..........",
578 		"x.x..x..........",
579 		"x.x..xxxxxxx....",
580 		"................",
581 		"................",
582 	};
583 
584 	static char *focus_xpm[] = {
585 		"16 12 2 1",
586 		"# c #ffffff",
587 		". c #000000",
588 		"................",
589 		"................",
590 		"###..........###",
591 		"#..............#",
592 		"#.....####.....#",
593 		".....#....#.....",
594 		".....#....#.....",
595 		"#.....####.....#",
596 		"#..............#",
597 		"###..........###",
598 		"................",
599 		"................",
600 	};
601 
602 	static char *flash_xpm[] = {
603 		"16 12 2 1",
604 		"# c #ffffff",
605 		". c #000000",
606 		"................",
607 		"..........#.....",
608 		"........##......",
609 		".......##.......",
610 		"......##........",
611 		".....########...",
612 		"..........##....",
613 		".......#.##.....",
614 		".......###......",
615 		".......####.....",
616 		"................",
617 		"................",
618 	};
619 
620 	static char *wb_xpm[] = {
621 		"16 12 2 1",
622 		"# c #ffffff",
623 		". c #000000",
624 		"................",
625 		"................",
626 		"................",
627 		"#.....#..####...",
628 		"#.....#..#...#..",
629 		"#..#..#..####...",
630 		"#..#..#..#...#..",
631 		".##.##...####...",
632 		"................",
633 		"................",
634 		"................",
635 		"................",
636 	};
637 /* Template for more xpm's:
638 	static char *empty_xpm[] = {
639 		"16 12 2 1",
640 		"# c #ffffff",
641 		". c #000000",
642 		"................",
643 		"................",
644 		"................",
645 		"................",
646 		"................",
647 		"................",
648 		"................",
649 		"................",
650 		"................",
651 		"................",
652 		"................",
653 		"................",
654 	};
655 */
656 	SDL_FillRect(m->screen, NULL, SDL_MapRGB( m->liveview->format, 0, 0, 0 ));
657 
658 	{
659 		/* Paint grid */
660 		int x, y;
661 		int nx = m->nx;
662 		for (x=1; x<nx; x++) {
663 			int x_ = (x*m->wx)/nx;
664 			sdl_line_color(m, x_, 1, x_, m->wy-1, SDL_MapRGB( m->liveview->format, 40, 40, 40 ));
665 		}
666 
667 		int ny = m->ny;
668 		for (y=1; y<nx; y++) {
669 			int y_ = (y*m->wy)/ny;
670 			sdl_line_color(m, 1, y_, m->wx-1, y_, SDL_MapRGB( m->liveview->format, 40, 40, 40 ));
671 		}
672 
673 	}
674 
675 	sdl_paint_image(m, wait_xpm,  m->wx/2,     m->wy/2);
676 
677 
678 	sdl_paint_button(m, af_xpm, 0,  0, m->do_focus);
679 	sdl_paint_button(m, ae_xpm, -1, 0, m->do_exposure);
680 
681 	sdl_paint_button(m, exit_xpm,   0, -1, 1);
682 	sdl_paint_button(m, flash_xpm,  1, -1, m->do_flash);
683 	sdl_paint_button(m, wb_xpm,     2, -1, m->do_white);
684 	sdl_paint_button(m, focus_xpm, -1, -2, 1);
685 	sdl_paint_button(m, ok_xpm,    -1, -1, 1);
686 
687 	sdl_paint_slider(m);
688 	SDL_UpdateWindowSurface(m->window);
689 }
690 
usec_to_time(double v)691 static double usec_to_time(double v)
692 {
693 	return 1/(v*.000001);
694 }
695 
sdl_status(struct sdl * m,double avg)696 static void sdl_status(struct sdl *m, double avg)
697 {
698 	int ox = 0;
699 	int oy = m->wy/2;
700 	int s = 3;
701 	SDL_Rect rcDest = { ox, oy, s*25, s*40 };
702 
703 	SDL_FillRect(m->screen, &rcDest, SDL_MapRGB( m->liveview->format, 30, 30, 30 ));
704 
705 	sdl_number(m, ox, oy, s, 3, avg*1000);
706 	oy += s*10;
707 
708 	{
709 		double focus, gain, exposure;
710 
711 		exposure = v4l2_g_ctrl(m->fd, V4L2_CID_EXPOSURE_ABSOLUTE);
712 		gain = cam_get_gain_iso(m->dev);
713 		focus = cam_get_focus_diopter(m);
714 
715 		double x = usec_to_time(exposure);
716 		if (x > 999) x = 999;
717 		sdl_number(m, ox, oy, s, 3, x);
718 
719 		oy += s*10;
720 		if (gain > 999)
721 			gain = 999;
722 		sdl_number(m, ox, oy, s, 3, gain);
723 
724 		oy += s*10;
725 		x = focus; /* diopters */
726 		if (x == 0)
727 			x = 999;
728 		else
729 			x = 100/x; /* centimeters */
730 		sdl_number(m, ox, oy, s, 3, x);
731 	}
732 
733 	SDL_UpdateWindowSurfaceRects(m->window, &rcDest, 1);
734 }
buf_pixel(struct v4l2_format * fmt,unsigned char * buf,int x,int y)735 static pixel buf_pixel(struct v4l2_format *fmt, unsigned char *buf, int x, int y)
736 {
737 	pixel p = { 0, 0, 0, 0 };
738 	int pos = x + y*fmt->fmt.pix.width;
739 
740 	p.alpha = 128;
741 
742 	switch (fmt->fmt.pix.pixelformat) {
743 	case V4L2_PIX_FMT_SGRBG10:
744 		{
745 			short *b2 = (void *)buf;
746 			x &= ~1;
747 			y &= ~1;
748 			p.g = b2[x + y*fmt->fmt.pix.width] /4;
749 			p.r = b2[x + y*fmt->fmt.pix.width+1] /4;
750 			p.b = b2[x + (y+1)*fmt->fmt.pix.width] /4;
751 		}
752 		break;
753 
754 	case V4L2_PIX_FMT_RGB24:
755 		pos *= 3;
756 		p.r = buf[pos];
757 		p.g = buf[pos+1];
758 		p.b = buf[pos+2];
759 		break;
760 
761 	default:
762 		printf("Wrong pixel format!\n");
763 		fmt_print(fmt);
764 		exit(1);
765 	}
766 
767 	return p;
768 }
769 
fmt_name(struct v4l2_format * fmt)770 static char *fmt_name(struct v4l2_format *fmt)
771 {
772 	switch (fmt->fmt.pix.pixelformat) {
773 	case V4L2_PIX_FMT_SGRBG10:
774 		return "GRBG10";
775 	case V4L2_PIX_FMT_RGB24:
776 		return "RGB24";
777 	default:
778 		return "unknown";
779 	}
780 }
781 
sdl_handle_focus(struct sdl * m,float how)782 static void sdl_handle_focus(struct sdl *m, float how)
783 {
784 	v4l2_set_control(m->fd, V4L2_CID_FOCUS_ABSOLUTE, 65535. * how);
785 }
786 
sdl_key(struct sdl * m,int c)787 static void sdl_key(struct sdl *m, int c)
788 {
789 	switch (c) {
790 	case 27: exit(1); break;
791 	case 'q': sdl_handle_focus(m, 0.); break;
792 	case 'w': sdl_handle_focus(m, 1/6.); break;
793 	case 'e': sdl_handle_focus(m, 1/3.); break;
794 	case 'r': sdl_handle_focus(m, 1/2.); break;
795 	case 't': sdl_handle_focus(m, 1/1); break;
796 	case 'y': sdl_handle_focus(m, 1/.8); break;
797 	case 'u': sdl_handle_focus(m, 1/.5); break;
798 	case 'i': sdl_handle_focus(m, 1/.2); break;
799 	case 'o': sdl_handle_focus(m, 1/.1); break;
800 	case 'p': sdl_handle_focus(m, 1/.05); break;
801 	default: printf("Unknown key %d / %c", c, c);
802 	}
803 }
804 
sdl_render_statistics(struct sdl * m)805 static int sdl_render_statistics(struct sdl *m)
806 {
807 	pixel white;
808 	double focus, gain, exposure;
809 
810 	white.r = (Uint8)0xff;
811 	white.g = (Uint8)0xff;
812 	white.b = (Uint8)0xff;
813 	white.alpha = (Uint8)128;
814 
815 	exposure = cam_get_exposure(m);
816 	gain = v4l2_get_control(m->fd, 0x00980913) / 65535.;
817 	focus = cam_get_focus(m);
818 
819 	for (int x=0; x<m->sx && x<m->sx*focus; x++)
820 		sfc_put_pixel(m->liveview, x, 0, &white);
821 
822 	for (int y=0; y<m->sy && y<m->sy*gain; y++)
823 		sfc_put_pixel(m->liveview, 0, y, &white);
824 
825 	for (int y=0; y<m->sy && y<m->sy*exposure; y++)
826 		sfc_put_pixel(m->liveview, m->sx-1, y, &white);
827 
828 	for (int x=0; x<m->sx; x++)
829 		sfc_put_pixel(m->liveview, x, m->sy-1, &white);
830 
831 	return 0;
832 }
833 
sdl_render(struct sdl * m,unsigned char * buf,struct v4l2_format * fmt)834 static void sdl_render(struct sdl *m, unsigned char *buf, struct v4l2_format *fmt)
835 {
836 	float zoom = m->zoom;
837 	if (!m->window)
838 		return;
839 
840 	sdl_begin_paint(m);
841 	int x0 = (fmt->fmt.pix.width - m->sx*m->factor/zoom)/2;
842 	int y0 = (fmt->fmt.pix.height - m->sy*m->factor/zoom)/2;
843 
844 	for (int y = 0; y < m->sy; y++)
845 		for (int x = 0; x < m->sx; x++) {
846 			int x1 = x0+x*m->factor/zoom;
847 			int y1 = y0+y*m->factor/zoom;
848 			pixel p = buf_pixel(fmt, buf, x1, y1);
849 			p.alpha = 128;
850 			sfc_put_pixel(m->liveview, x, y, &p);
851 		}
852 
853 	sdl_render_statistics(m);
854 	sdl_finish_paint(m);
855 }
856 
sdl_sync_settings(struct sdl * m)857 static void sdl_sync_settings(struct sdl *m)
858 {
859 	printf("Autofocus: "); v4l2_s_ctrl(m->fd, V4L2_CID_FOCUS_AUTO, m->do_focus);
860 	printf("Autogain: " ); v4l2_s_ctrl(m->fd, V4L2_CID_AUTOGAIN, m->do_exposure);
861 	printf("Autowhite: "); v4l2_s_ctrl(m->fd, V4L2_CID_AUTO_WHITE_BALANCE, m->do_white);
862 	v4l2_s_ctrl(m->fd, 0x009c0901, m->do_flash ? 2 : 0);
863 }
864 
sdl_init_window(struct sdl * m)865 static void sdl_init_window(struct sdl *m)
866 {
867 	if (m->do_full) {
868 		m->wx = 800;
869 		m->wy = 480;
870 	} else {
871 		m->wx = 800;
872 		m->wy = 429;
873 	}
874 
875 	SDL_SetWindowFullscreen(m->window, m->do_full*SDL_WINDOW_FULLSCREEN_DESKTOP);
876 
877 	m->screen = SDL_GetWindowSurface(m->window);
878 	if (!m->screen) {
879 		printf("Couldn't create screen\n");
880 		exit(1);
881 	}
882 }
883 
sdl_init(struct sdl * m,struct dev_info * dev)884 static void sdl_init(struct sdl *m, struct dev_info *dev)
885 {
886 	m->fd = dev->fd;
887 	m->dev = dev;
888 
889 	if (SDL_Init(SDL_INIT_VIDEO) < 0) {
890 		printf("Could not init SDL\n");
891 		exit(1);
892 	}
893 
894 	atexit(SDL_Quit);
895 
896 	m->nx = 6;
897 	m->ny = 4;
898 
899 	m->do_flash = 1;
900 	m->do_focus = 0;
901 	m->do_exposure = 1;
902 	m->focus_min = 5;
903 	m->do_white = 0;
904 	m->do_big = 0;
905 	m->do_full = 0;
906 	m->zoom = 1;
907 
908 	m->window = SDL_CreateWindow("Camera", SDL_WINDOWPOS_UNDEFINED,
909 				     SDL_WINDOWPOS_UNDEFINED, 800, 429,
910 				     SDL_WINDOW_SHOWN |
911 				     m->do_full * SDL_WINDOW_FULLSCREEN_DESKTOP);
912 	if (m->window == NULL) {
913 		printf("Window could not be created! SDL_Error: %s\n", SDL_GetError());
914 		exit(1);
915 	}
916 
917 	sdl_init_window(m);
918 }
919 
sdl_set_size(struct sdl * m,int _sx)920 static void sdl_set_size(struct sdl *m, int _sx)
921 {
922 	m->sx = _sx;
923 	m->factor = (float) m->dev->fmt.fmt.pix.width / m->sx;
924 	m->sy = m->dev->fmt.fmt.pix.height / m->factor;
925 
926 	m->bx = (m->wx-m->sx)/2;
927 	m->by = (m->wy-m->sy)/2;
928 
929 	m->liveview = SDL_CreateRGBSurface(0,m->sx,m->sy,32,0,0,0,0);
930 	if (!m->liveview) {
931 		printf("Couldn't create liveview\n");
932 		exit(1);
933 	}
934 
935 	sdl_paint_ui(m);
936 	sdl_sync_settings(m);
937 }
938 
939 static struct sdl sdl;
940 
ppm_write(struct v4l2_format * fmt,unsigned char * img,char * out_name)941 static void ppm_write(struct v4l2_format *fmt, unsigned char *img, char *out_name)
942 {
943 	FILE *fout;
944 	fout = fopen(out_name, "w");
945 	if (!fout) {
946 		perror("Cannot open image");
947 		exit(EXIT_FAILURE);
948 	}
949 	switch (fmt->fmt.pix.pixelformat) {
950 	case V4L2_PIX_FMT_RGB24:
951 		printf("ok\n");
952 		break;
953 	default:
954 		printf("Bad pixel format\n");
955 		exit(1);
956 	}
957 
958 	fprintf(fout, "P6\n%d %d 255\n",
959 		fmt->fmt.pix.width, fmt->fmt.pix.height);
960 	fwrite(img, fmt->fmt.pix.width, 3*fmt->fmt.pix.height, fout);
961 	fclose(fout);
962 }
963 
964 /**
965    Write image to jpeg file.
966    \param img image to write
967 */
jpeg_write(struct v4l2_format * fmt,unsigned char * img,char * jpegFilename)968 static void jpeg_write(struct v4l2_format *fmt, unsigned char *img, char *jpegFilename)
969 {
970 	struct jpeg_compress_struct cinfo;
971 	struct jpeg_error_mgr jerr;
972 
973 	JSAMPROW row_pointer[1];
974 	FILE *outfile = fopen(jpegFilename, "wb");
975 	if (!outfile) {
976 		printf("Can't open jpeg for writing.\n");
977 		exit(2);
978 	}
979 
980 	/* create jpeg data */
981 	cinfo.err = jpeg_std_error(&jerr);
982 	jpeg_create_compress(&cinfo);
983 	jpeg_stdio_dest(&cinfo, outfile);
984 
985 	/* set image parameters */
986 	cinfo.image_width = fmt->fmt.pix.width;
987 	cinfo.image_height = fmt->fmt.pix.height;
988 	cinfo.input_components = 3;
989 	switch (fmt->fmt.pix.pixelformat) {
990 	case V4L2_PIX_FMT_SGRBG10:
991 	case V4L2_PIX_FMT_RGB24:
992 		cinfo.in_color_space = JCS_RGB;
993 		break;
994 	default:
995 		printf("Need to specify colorspace!\n");
996 		exit(1);
997 	}
998 
999 	/* set jpeg compression parameters to default */
1000 	jpeg_set_defaults(&cinfo);
1001 	jpeg_set_quality(&cinfo, 90, TRUE);
1002 
1003 	jpeg_start_compress(&cinfo, TRUE);
1004 
1005 	/* feed data */
1006 	while (cinfo.next_scanline < cinfo.image_height) {
1007 		row_pointer[0] = &img[cinfo.next_scanline * cinfo.image_width *  cinfo.input_components];
1008 		jpeg_write_scanlines(&cinfo, row_pointer, 1);
1009 	}
1010 
1011 	jpeg_finish_compress(&cinfo);
1012 	jpeg_destroy_compress(&cinfo);
1013 	fclose(outfile);
1014 }
1015 
any_write(struct dev_info * dev)1016 static void any_write(struct dev_info *dev)
1017 {
1018 	char name[1024];
1019 	unsigned char *buf;
1020 	time_t t = time(NULL);
1021 	int ppm = 0;
1022 
1023 	buf = dev->buf;
1024 
1025 	sprintf(name, PICDIR "/delme_%d.%s", (int) t, ppm ? "ppm" : "jpg");
1026 	if (dev->fmt.fmt.pix.pixelformat != V4L2_PIX_FMT_RGB24) {
1027 		printf("Wrong pixel format\n");
1028 		exit(1);
1029 	}
1030 	if (ppm)
1031 		ppm_write(&dev->fmt, buf, name);
1032 	else
1033 		jpeg_write(&dev->fmt, buf, name);
1034 }
1035 
sdl_mouse(struct sdl * m,SDL_Event event)1036 static void sdl_mouse(struct sdl *m, SDL_Event event)
1037 {
1038 	int ax = 0, ay = 0;
1039 	int nx = event.button.x / (m->wx/m->nx), ny = event.button.y / (m->wy/m->ny);
1040 	if (nx >= m->nx/2)
1041 		nx -= m->nx;
1042 	if (ny >= m->ny/2)
1043 		ny -= m->ny;
1044 
1045 	printf("Button %d %d\n", nx, ny);
1046 
1047 	/* Virtual slider */
1048 	if (nx == -2) {
1049 		double value = (double) event.button.y / m->wy;
1050 		switch (m->slider_mode) {
1051 		case M_BIAS: {
1052 			double ev = value - .5; /* -.5 .. +.5 */
1053 			ev *= 3000;
1054 			printf("Exposure bias: %f mEV %d\n", ev, (int) ev);
1055 			if (v4l2_s_ctrl(m->fd, V4L2_CID_AUTO_EXPOSURE_BIAS, ev) < 0) {
1056 				printf("Could not set exposure bias\n");
1057 			}
1058 		}
1059 			return;
1060 		case M_EXPOSURE:
1061 			m->do_exposure = 0;
1062 			cam_set_exposure(m, value);
1063 			sdl_sync_settings(m);
1064 			return;
1065 		case M_GAIN:
1066 			m->do_exposure = 0;
1067 			v4l2_set_control(m->fd, 0x00980913, value * 65535);
1068 			sdl_sync_settings(m);
1069 			return;
1070 		case M_FOCUS:
1071 			cam_set_focus(m, value);
1072 			return;
1073 		}
1074 	}
1075 
1076 	switch (ny) {
1077 	case 0:
1078 		switch (nx) {
1079 		case 0:
1080 			m->do_focus ^= 1;
1081 			sdl_paint_ui(m);
1082 			sdl_sync_settings(m);
1083 			return;
1084 		case -1:
1085 			m->do_exposure ^= 1;
1086 			sdl_paint_ui(m);
1087 			sdl_sync_settings(m);
1088 			return;
1089 		}
1090 		break;
1091 	case 1:
1092 		switch (nx) {
1093 		case -1:
1094 			m->slider_mode = (m->slider_mode + 1) % M_NUM;
1095 			sdl_paint_ui(m);
1096 		}
1097 		break;
1098 	case -2:
1099 		switch (nx) {
1100 		case -1:
1101 			v4l2_s_ctrl(m->fd, V4L2_CID_AUTO_FOCUS_STATUS, 1);
1102 			return;
1103 		break;
1104 		}
1105 	case -1:
1106 		switch (nx) {
1107 		case 0:
1108 			exit(0);
1109 		case 1:
1110 			m->do_flash ^= 1;
1111 			sdl_paint_ui(m);
1112 			sdl_sync_settings(m);
1113 			return;
1114 		case 2:
1115 			m->do_white ^= 1;
1116 			sdl_paint_ui(m);
1117 			sdl_sync_settings(m);
1118 			return;
1119 		case -3:
1120 			m->do_big ^= 1;
1121 			if (m->do_big)
1122 				sdl_set_size(m, m->do_full ? 630:512);
1123 			else
1124 				sdl_set_size(m, 256);
1125 			return;
1126 		case -1:
1127 			sdl_paint_ui(m);
1128 			any_write(m->dev);
1129 			return;
1130 		}
1131 		break;
1132 	}
1133 
1134 	if (event.button.x > m->wx-m->bx)
1135 		ax = 1;
1136 	if (event.button.x < m->bx)
1137 		ax = -1;
1138 
1139 	if (event.button.y > m->wy-m->by)
1140 		ay = 1;
1141 	if (event.button.y < m->by)
1142 		ay = -1;
1143 
1144 	printf("mouse button at...%d, %d area %d, %d\n", event.button.x, event.button.y,
1145 	       ax, ay);
1146 }
1147 
sdl_iteration(struct sdl * m)1148 static void sdl_iteration(struct sdl *m)
1149 {
1150 	SDL_Event event;
1151 
1152 	while(SDL_PollEvent(&event)) {
1153 		switch(event.type) {
1154 		case SDL_QUIT:
1155 			exit(1);
1156 			break;
1157 		case SDL_KEYDOWN:
1158 			printf("key pressed... %c\n", event.key.keysym.sym);
1159 			/* SDLK_A, SDLK_LEFT, SDLK_RETURN, SDLK_BACKSPACE, SDLK_SPACE */
1160 			switch (event.key.keysym.sym) {
1161 			default: sdl_key(m, event.key.keysym.sym);
1162 			}
1163 			break;
1164 		case SDL_WINDOWEVENT:
1165 			if (event.window.event == SDL_WINDOWEVENT_EXPOSED)
1166 				sdl_paint_ui(m);
1167 			break;
1168 		case SDL_MOUSEBUTTONDOWN:
1169 			sdl_mouse(m, event);
1170 			break;
1171 		}
1172 	}
1173 }
1174 
cam_open(struct dev_info * dev,char * name)1175 static void cam_open(struct dev_info *dev, char *name)
1176 {
1177 	struct v4l2_format *fmt = &dev->fmt;
1178 
1179 	dev->fd = v4l2_open(name, O_RDWR);
1180 	if (dev->fd < 0) {
1181 		printf("video %s open failed: %m\n", name);
1182 		exit(1);
1183 	}
1184 
1185 	fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1186 	fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
1187 	fmt->fmt.pix.field = V4L2_FIELD_NONE;
1188 	fmt->fmt.pix.width = SX;
1189 	fmt->fmt.pix.height = SY;
1190 
1191 	v4l2_s_ctrl(dev->fd, V4L2_CID_AUTO_FOCUS_STATUS, 0);
1192 	v4l2_set_focus(dev->fd, 50);
1193 
1194 	printf("ioctl = %d\n", v4l2_ioctl(dev->fd, VIDIOC_S_FMT, fmt));
1195 
1196 	printf("capture is %lx %s %d x %d\n", (unsigned long) fmt->fmt.pix.pixelformat, fmt_name(fmt), fmt->fmt.pix.width, fmt->fmt.pix.height);
1197 }
1198 
1199 /* ------------------------------------------------------------------ */
1200 
1201 
1202 static struct dev_info dev;
1203 
main(void)1204 int main(void)
1205 {
1206 	int i;
1207 	struct v4l2_format *fmt = &dev.fmt;
1208 
1209 	dtime();
1210 	cam_open(&dev, "/dev/video0");
1211 
1212 	sdl_init(&sdl, &dev);
1213 	sdl_set_size(&sdl, 256);
1214 
1215 	double loop = dtime(), max = 0, avg = .200;
1216 	if (dev.debug & D_TIMING)
1217 		printf("startup took %f\n", loop);
1218 
1219 	for (i=0; i<500000; i++) {
1220 		int num = v4l2_read(dev.fd, dev.buf, SIZE);
1221 		if (num < 0) {
1222 			printf("Could not read frame\n");
1223 			return 1;
1224 		}
1225 		{
1226 			double d = dtime();
1227 			sdl_render(&sdl, dev.buf, fmt);
1228 			if (dev.debug & D_TIMING)
1229 				printf("Render took %f\n", dtime() - d);
1230 		}
1231 		{
1232 			double d = dtime();
1233 			for (int i = 0; i<1; i++)
1234 				sdl_status(&sdl, avg);
1235 			if (dev.debug & D_TIMING)
1236 				printf("Status took %f\n", dtime() - d);
1237 		}
1238 
1239 		sdl_iteration(&sdl);
1240 		double now = dtime();
1241 		if (now - loop > max)
1242 			max = now - loop;
1243 		double c = 0.03;
1244 		avg = (now - loop) * c + avg * (1-c);
1245 		if (dev.debug & D_TIMING)
1246 			printf("Iteration %f, maximum %f, average %f\n", now-loop, max, avg);
1247 		loop = now;
1248 	}
1249 	return 0;
1250 }
1251