Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592/* quirc -- QR-code recognition library * Copyright (C) 2010-2014 Daniel Beer <dlbeer@gmail.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
#include <stdint.h>#include <string.h>#include <fcntl.h>#include <errno.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/ioctl.h>#include <sys/mman.h>#include <unistd.h>#ifdef __OpenBSD__#include <sys/videoio.h>#else#include <linux/videodev2.h>#endif#include "camera.h"
/************************************************************************ * Fraction arithmetic */
static int gcd(int a, int b){ if (a < 0) a = -a; if (b < 0) b = -b;
for (;;) { if (a < b) { const int t = a;
a = b; b = t; }
if (!b) break;
a %= b; }
return a;}
static void frac_reduce(const struct v4l2_fract *f, struct v4l2_fract *g){ const int x = gcd(f->numerator, f->denominator); int n = f->numerator; int d = f->denominator;
if (d < 0) { n = -n; d = -d; }
g->numerator = n / x; g->denominator = d / x;}
static void frac_add(const struct v4l2_fract *a, const struct v4l2_fract *b, struct v4l2_fract *r){ r->numerator = a->numerator * b->denominator + b->numerator * b->denominator; r->denominator = a->denominator * b->denominator;
frac_reduce(r, r);}
static void frac_sub(const struct v4l2_fract *a, const struct v4l2_fract *b, struct v4l2_fract *r){ r->numerator = a->numerator * b->denominator - b->numerator * b->denominator; r->denominator = a->denominator * b->denominator;
frac_reduce(r, r);}
static int frac_cmp(const struct v4l2_fract *a, const struct v4l2_fract *b){ return a->numerator * b->denominator - b->numerator * b->denominator;}
static void frac_mul(const struct v4l2_fract *a, const struct v4l2_fract *b, struct v4l2_fract *r){ r->numerator = a->numerator * b->numerator; r->denominator = a->denominator * b->denominator;
frac_reduce(r, r);}
static void frac_div(const struct v4l2_fract *a, const struct v4l2_fract *b, struct v4l2_fract *r){ r->numerator = a->numerator * b->denominator; r->denominator = a->denominator * b->numerator;
frac_reduce(r, r);}
static int frac_cmp_ref(const struct v4l2_fract *ref, const struct v4l2_fract *a, const struct v4l2_fract *b){ struct v4l2_fract da; struct v4l2_fract db;
frac_sub(a, ref, &da); frac_sub(b, ref, &db);
if (da.numerator < 0) da.numerator = -da.numerator; if (db.numerator < 0) db.numerator = -db.numerator;
return frac_cmp(&da, &db);}
/************************************************************************ * Parameter searching and choosing */
static camera_format_t map_fmt(uint32_t pf){ if (pf == V4L2_PIX_FMT_YUYV) return CAMERA_FORMAT_YUYV;
if (pf == V4L2_PIX_FMT_MJPEG) return CAMERA_FORMAT_MJPEG;
return CAMERA_FORMAT_UNKNOWN;}
static int find_best_format(int fd, uint32_t *fmt_ret){ struct v4l2_fmtdesc best; int i = 1;
memset(&best, 0, sizeof(best)); best.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; best.index = 0;
if (ioctl(fd, VIDIOC_ENUM_FMT, &best) < 0) return -1;
for (;;) { struct v4l2_fmtdesc f;
memset(&f, 0, sizeof(f)); f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f.index = ++i;
if (ioctl(fd, VIDIOC_ENUM_FMT, &f) < 0) break;
if (map_fmt(f.pixelformat) > map_fmt(best.pixelformat)) memcpy(&best, &f, sizeof(best)); }
if (fmt_ret) *fmt_ret = best.pixelformat;
return 0;}
static int step_to_discrete(int min, int max, int step, int target){ int offset;
if (target < min) return min;
if (target > max) return max;
offset = (target - min) % step; if ((offset * 2 > step) && (target + step <= max)) target += step;
return target - offset;}
static int score_discrete(const struct v4l2_frmsizeenum *f, int w, int h){ const int dw = f->discrete.width - w; const int dh = f->discrete.height - h;
return dw * dw + dh * dh;}
static int find_best_size(int fd, uint32_t pixel_format, int target_w, int target_h, int *ret_w, int *ret_h){ struct v4l2_frmsizeenum best; int i = 1;
memset(&best, 0, sizeof(best)); best.index = 0; best.pixel_format = pixel_format;
if (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &best) < 0) return -1;
if (best.type != V4L2_FRMSIZE_TYPE_DISCRETE) { *ret_w = step_to_discrete(best.stepwise.min_width, best.stepwise.max_width, best.stepwise.step_width, target_w); *ret_h = step_to_discrete(best.stepwise.min_height, best.stepwise.max_height, best.stepwise.step_height, target_h); return 0; }
for (;;) { struct v4l2_frmsizeenum f;
memset(&f, 0, sizeof(f)); f.index = ++i; f.pixel_format = pixel_format;
if (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &f) < 0) break;
if (score_discrete(&f, target_w, target_h) < score_discrete(&best, target_w, target_h)) memcpy(&best, &f, sizeof(best)); }
*ret_w = best.discrete.width; *ret_h = best.discrete.height;
return 0;}
static int find_best_rate(int fd, uint32_t pixel_format, int w, int h, int target_n, int target_d, int *ret_n, int *ret_d){ const struct v4l2_fract target = { .numerator = target_n, .denominator = target_d }; struct v4l2_frmivalenum best; int i = 1;
memset(&best, 0, sizeof(best)); best.index = 0; best.pixel_format = pixel_format; best.width = w; best.height = h;
if (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &best) < 0) return -1;
if (best.type != V4L2_FRMIVAL_TYPE_DISCRETE) { struct v4l2_fract t;
if (frac_cmp(&target, &best.stepwise.min) < 0) { *ret_n = best.stepwise.min.numerator; *ret_d = best.stepwise.min.denominator; }
if (frac_cmp(&target, &best.stepwise.max) > 0) { *ret_n = best.stepwise.max.numerator; *ret_d = best.stepwise.max.denominator; }
frac_sub(&target, &best.stepwise.min, &t); frac_div(&t, &best.stepwise.step, &t); if (t.numerator * 2 >= t.denominator) t.numerator += t.denominator; t.numerator /= t.denominator; t.denominator = 1; frac_mul(&t, &best.stepwise.step, &t); frac_add(&t, &best.stepwise.max, &t);
if (frac_cmp(&t, &best.stepwise.max) > 0) { *ret_n = best.stepwise.max.numerator; *ret_d = best.stepwise.max.denominator; } else { *ret_n = t.numerator; *ret_d = t.denominator; }
return 0; }
for (;;) { struct v4l2_frmivalenum f;
memset(&f, 0, sizeof(f)); f.index = ++i; f.pixel_format = pixel_format; f.width = w; f.height = h;
if (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &f) < 0) break;
if (frac_cmp_ref(&target, &f.discrete, &best.discrete) < 0) memcpy(&best, &f, sizeof(best)); }
*ret_n = best.discrete.numerator; *ret_d = best.discrete.denominator;
return 0;}
/************************************************************************ * Public interface */
void camera_init(struct camera *c){ c->fd = -1; c->buf_count = 0; c->s_on = 0;}
void camera_destroy(struct camera *c){ camera_close(c);}
int camera_open(struct camera *c, const char *path, int target_w, int target_h, int tr_n, int tr_d){ struct v4l2_format fmt; struct v4l2_streamparm parm; uint32_t pf; int w, h; int n, d;
if (c->fd >= 0) camera_close(c);
/* Open device and get basic properties */ c->fd = open(path, O_RDWR); if (c->fd < 0) return -1;
/* Find a pixel format from the list */ if (find_best_format(c->fd, &pf) < 0) goto fail;
/* Find a frame size */ if (find_best_size(c->fd, pf, target_w, target_h, &w, &h) < 0) goto fail;
/* Find a frame rate */ if (find_best_rate(c->fd, pf, w, h, tr_n, tr_d, &n, &d) < 0) goto fail;
/* Set format */ memset(&fmt, 0, sizeof(fmt)); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = w; fmt.fmt.pix.height = h; fmt.fmt.pix.pixelformat = pf; if (ioctl(c->fd, VIDIOC_S_FMT, &fmt) < 0) goto fail;
memset(&fmt, 0, sizeof(fmt)); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (ioctl(c->fd, VIDIOC_G_FMT, &fmt) < 0) goto fail;
/* Set frame interval */ memset(&parm, 0, sizeof(parm)); parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; parm.parm.capture.timeperframe.numerator = n; parm.parm.capture.timeperframe.denominator = d; if (ioctl(c->fd, VIDIOC_S_PARM, &parm) < 0) goto fail;
memset(&parm, 0, sizeof(parm)); parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (ioctl(c->fd, VIDIOC_G_PARM, &parm) < 0) goto fail;
c->parms.format = map_fmt(fmt.fmt.pix.pixelformat); c->parms.width = fmt.fmt.pix.width; c->parms.height = fmt.fmt.pix.height; c->parms.pitch_bytes = fmt.fmt.pix.bytesperline; c->parms.interval_n = parm.parm.capture.timeperframe.numerator; c->parms.interval_d = parm.parm.capture.timeperframe.denominator;
return 0;
fail: { const int e = errno;
close(c->fd); c->fd = -1; errno = e; }
return -1;}
void camera_close(struct camera *c){ camera_off(c); camera_unmap(c);
if (c->fd < 0) return;
close(c->fd); c->fd = -1;}
int camera_map(struct camera *c, int buf_count){ struct v4l2_requestbuffers reqbuf; int count; int i;
if (buf_count > CAMERA_MAX_BUFFERS) buf_count = CAMERA_MAX_BUFFERS;
if (buf_count <= 0) { errno = EINVAL; return -1; }
if (c->fd < 0) { errno = EBADF; return -1; }
if (c->buf_count) camera_unmap(c);
memset(&reqbuf, 0, sizeof(reqbuf)); reqbuf.count = buf_count; reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; reqbuf.memory = V4L2_MEMORY_MMAP;
if (ioctl(c->fd, VIDIOC_REQBUFS, &reqbuf) < 0) return -1;
count = reqbuf.count; if (count > CAMERA_MAX_BUFFERS) count = CAMERA_MAX_BUFFERS;
/* Query all buffers */ for (i = 0; i < count; i++) { struct v4l2_buffer buf; struct camera_buffer *cb = &c->buf_desc[i];
memset(&buf, 0, sizeof(buf)); buf.index = i; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; if (ioctl(c->fd, VIDIOC_QUERYBUF, &buf) < 0) return -1;
cb->offset = buf.m.offset; cb->size = buf.length; cb->addr = mmap(NULL, cb->size, PROT_READ, MAP_SHARED, c->fd, cb->offset);
if (cb->addr == MAP_FAILED) { const int save = errno;
i--; while (i >= 0) { cb = &c->buf_desc[i--]; munmap(cb->addr, cb->size); }
errno = save; return -1; } }
c->buf_count = count; return 0;}
void camera_unmap(struct camera *c){ int i;
for (i = 0; i < c->buf_count; i++) { struct camera_buffer *cb = &c->buf_desc[i];
munmap(cb->addr, cb->size); }
c->buf_count = 0;}
int camera_on(struct camera *c){ int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (c->s_on) return 0;
if (c->fd < 0) { errno = EBADF; return -1; }
if (ioctl(c->fd, VIDIOC_STREAMON, &type) < 0) return -1;
c->s_on = 1; c->s_qc = 0; c->s_qhead = 0; return 0;}
void camera_off(struct camera *c){ int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (!c->s_on) return;
ioctl(c->fd, VIDIOC_STREAMOFF, &type); c->s_on = 0;}
int camera_enqueue_all(struct camera *c){ while (c->s_qc < c->buf_count) { struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf)); buf.index = (c->s_qc + c->s_qhead) % c->buf_count; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP;
if (ioctl(c->fd, VIDIOC_QBUF, &buf) < 0) return -1;
c->s_qc++; }
return 0;}
int camera_dequeue_one(struct camera *c){ struct v4l2_buffer buf;
if (!c->s_qc) { errno = EINVAL; return -1; }
memset(&buf, 0, sizeof(buf)); buf.memory = V4L2_MEMORY_MMAP; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(c->fd, VIDIOC_DQBUF, &buf) < 0) return -1;
c->s_qc--; if (++c->s_qhead >= c->buf_count) c->s_qhead = 0;
return 0;}