demo: rewrite camera driver.
New camera driver doesn't depend on obsolete headers (linux/videodev.h), and should work on more types of cameras.
This commit is contained in:
parent
42c79481e7
commit
3a3df0d1d6
4 changed files with 699 additions and 237 deletions
690
demo/camera.c
690
demo/camera.c
|
@ -1,5 +1,5 @@
|
|||
/* quirc -- QR-code recognition library
|
||||
* Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
|
||||
* 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
|
||||
|
@ -14,208 +14,574 @@
|
|||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/videodev.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include "camera.h"
|
||||
|
||||
static int set_video_format(struct camera *cam, uint32_t format,
|
||||
int w, int h, camera_format_t my_fmt)
|
||||
/************************************************************************
|
||||
* 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 = format;
|
||||
fmt.fmt.pix.field = V4L2_FIELD_ANY;
|
||||
|
||||
if (ioctl(cam->fd, VIDIOC_S_FMT, &fmt) < 0)
|
||||
return -1;
|
||||
|
||||
/* Note that the size obtained may not match what was requested. */
|
||||
cam->format = my_fmt;
|
||||
cam->width = fmt.fmt.pix.width;
|
||||
cam->height = fmt.fmt.pix.height;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int camera_set_gain(struct camera *cam, int value)
|
||||
{
|
||||
struct v4l2_queryctrl query;
|
||||
struct v4l2_control ctrl;
|
||||
|
||||
query.id = V4L2_CID_GAIN;
|
||||
if (ioctl(cam->fd, VIDIOC_QUERYCTRL, &query) < 0) {
|
||||
perror("ioctl: VIDIOC_QUERYCTRL");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctrl.id = V4L2_CID_GAIN;
|
||||
|
||||
if (value < 0)
|
||||
ctrl.value = query.default_value;
|
||||
else
|
||||
ctrl.value = value;
|
||||
|
||||
if (ioctl(cam->fd, VIDIOC_S_CTRL, &ctrl) < 0) {
|
||||
perror("ioctl: VIDIOC_S_CTRL");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int camera_init(struct camera *cam, const char *path, int width, int height)
|
||||
{
|
||||
struct v4l2_streamparm fps;
|
||||
struct v4l2_requestbuffers rb;
|
||||
int type;
|
||||
int i;
|
||||
|
||||
cam->fd = open(path, O_RDONLY);
|
||||
if (cam->fd < 0) {
|
||||
fprintf(stderr, "%s: can't open camera device: %s\n", path,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set the video format */
|
||||
if (set_video_format(cam, V4L2_PIX_FMT_MJPEG,
|
||||
width, height, CAMERA_FORMAT_MJPEG) < 0 &&
|
||||
set_video_format(cam, V4L2_PIX_FMT_YUYV,
|
||||
width, height, CAMERA_FORMAT_YUYV) < 0) {
|
||||
fprintf(stderr, "%s: can't find a supported video format\n",
|
||||
path);
|
||||
fmt.fmt.pix.pixelformat = pf;
|
||||
if (ioctl(c->fd, VIDIOC_S_FMT, &fmt) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Set the frame rate */
|
||||
memset(&fps, 0, sizeof(fps));
|
||||
fps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
fps.parm.capture.timeperframe.numerator = 1;
|
||||
fps.parm.capture.timeperframe.denominator = 20;
|
||||
|
||||
if (ioctl(cam->fd, VIDIOC_S_PARM, &fps) < 0) {
|
||||
fprintf(stderr, "%s: can't set video frame rate: %s\n", path,
|
||||
strerror(errno));
|
||||
memset(&fmt, 0, sizeof(fmt));
|
||||
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
if (ioctl(c->fd, VIDIOC_G_FMT, &fmt) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Request buffer */
|
||||
memset(&rb, 0, sizeof(rb));
|
||||
rb.count = CAMERA_BUFFERS;
|
||||
rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
rb.memory = V4L2_MEMORY_MMAP;
|
||||
|
||||
if (ioctl(cam->fd, VIDIOC_REQBUFS, &rb) < 0) {
|
||||
fprintf(stderr, "%s: can't request buffers: %s\n", path,
|
||||
strerror(errno));
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Map buffers and submit them */
|
||||
for (i = 0; i < CAMERA_BUFFERS; i++) {
|
||||
struct v4l2_buffer buf;
|
||||
|
||||
memset(&buf, 0, sizeof(buf));
|
||||
buf.index = i;
|
||||
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
buf.memory = V4L2_MEMORY_MMAP;
|
||||
if (ioctl(cam->fd, VIDIOC_QUERYBUF, &buf) < 0) {
|
||||
fprintf(stderr, "%s: can't query buffer: %s\n", path,
|
||||
strerror(errno));
|
||||
memset(&parm, 0, sizeof(parm));
|
||||
parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
if (ioctl(c->fd, VIDIOC_G_PARM, &parm) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
cam->buffer_maps[i] = mmap(0, buf.length,
|
||||
PROT_READ, MAP_SHARED, cam->fd, buf.m.offset);
|
||||
if (cam->buffer_maps[i] == MAP_FAILED) {
|
||||
fprintf(stderr, "%s: can't map buffer memory: %s\n",
|
||||
path, strerror(errno));
|
||||
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;
|
||||
|
||||
cam->buffer_lens[i] = buf.length;
|
||||
}
|
||||
|
||||
/* Switch on streaming */
|
||||
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
if (ioctl(cam->fd, VIDIOC_STREAMON, &type) < 0) {
|
||||
fprintf(stderr, "%s: can't enable streaming: %s\n", path,
|
||||
strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Queue the buffers */
|
||||
for (i = 0; i < CAMERA_BUFFERS; i++) {
|
||||
struct v4l2_buffer buf;
|
||||
|
||||
memset(&buf, 0, sizeof(buf));
|
||||
buf.index = i;
|
||||
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
buf.memory = V4L2_MEMORY_MMAP;
|
||||
|
||||
if (ioctl(cam->fd, VIDIOC_QBUF, &buf) < 0) {
|
||||
perror("can't start buffer queue");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
cam->mem_index = -1;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
close(cam->fd);
|
||||
{
|
||||
const int e = errno;
|
||||
|
||||
close(c->fd);
|
||||
c->fd = -1;
|
||||
errno = e;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void camera_free(struct camera *cam)
|
||||
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 < CAMERA_BUFFERS; i++)
|
||||
munmap(cam->buffer_maps[i], cam->buffer_lens[i]);
|
||||
close(cam->fd);
|
||||
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_update(struct camera *cam)
|
||||
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;
|
||||
int last_index = cam->mem_index;
|
||||
|
||||
/* De-queue a buffer */
|
||||
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(cam->fd, VIDIOC_DQBUF, &buf) < 0) {
|
||||
perror("can't de-queue buffer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cam->mem_index = buf.index;
|
||||
cam->mem = cam->buffer_maps[cam->mem_index];
|
||||
cam->mem_len = cam->buffer_lens[cam->mem_index];
|
||||
|
||||
/* Re-queue the last buffer */
|
||||
if (last_index >= 0) {
|
||||
memset(&buf, 0, sizeof(buf));
|
||||
buf.index = last_index;
|
||||
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
buf.memory = V4L2_MEMORY_MMAP;
|
||||
if (ioctl(cam->fd, VIDIOC_QBUF, &buf) < 0) {
|
||||
perror("can't queue buffer");
|
||||
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;
|
||||
}
|
||||
|
|
106
demo/camera.h
106
demo/camera.h
|
@ -1,5 +1,5 @@
|
|||
/* quirc -- QR-code recognition library
|
||||
* Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
|
||||
* 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
|
||||
|
@ -17,54 +17,88 @@
|
|||
#ifndef CAMERA_H_
|
||||
#define CAMERA_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define CAMERA_BUFFERS 8
|
||||
#define CAMERA_MAX_BUFFERS 32
|
||||
|
||||
typedef enum {
|
||||
CAMERA_FORMAT_UNKNOWN = 0,
|
||||
CAMERA_FORMAT_MJPEG,
|
||||
CAMERA_FORMAT_YUYV
|
||||
} camera_format_t;
|
||||
|
||||
struct camera {
|
||||
/* File descriptor for camera device. */
|
||||
int fd;
|
||||
|
||||
/* Width, height and frame pixel format. Once initialized,
|
||||
* these values will not change.
|
||||
*/
|
||||
struct camera_parms {
|
||||
camera_format_t format;
|
||||
int width;
|
||||
int height;
|
||||
camera_format_t format;
|
||||
|
||||
/* Buffer information for the current frame. If mem_index < 0,
|
||||
* then no frame is currently available.
|
||||
*/
|
||||
uint8_t *mem;
|
||||
int mem_len;
|
||||
int mem_index;
|
||||
|
||||
/* Pointers and sizes of memory-mapped buffers. */
|
||||
uint8_t *buffer_maps[CAMERA_BUFFERS];
|
||||
int buffer_lens[CAMERA_BUFFERS];
|
||||
int pitch_bytes;
|
||||
int interval_n;
|
||||
int interval_d;
|
||||
};
|
||||
|
||||
/* Set up a camera driver. This opens the video device and configures for
|
||||
* the given resolution. Streaming is started immediately.
|
||||
*
|
||||
* Returns 0 on success or -1 if an error occurs.
|
||||
*/
|
||||
int camera_init(struct camera *cam, const char *path, int width, int height);
|
||||
struct camera_buffer {
|
||||
void *addr;
|
||||
size_t size;
|
||||
unsigned long offset;
|
||||
};
|
||||
|
||||
/* Set the gain control for the camera device. */
|
||||
int camera_set_gain(struct camera *cam, int gain);
|
||||
struct camera {
|
||||
int fd;
|
||||
|
||||
/* Shut down the camera device and free allocated memory. */
|
||||
void camera_free(struct camera *cam);
|
||||
struct camera_parms parms;
|
||||
|
||||
/* Retrieve the latest frame from the video queue and fill out the mem and
|
||||
* mem_len fields in the camera struct.
|
||||
*/
|
||||
int camera_update(struct camera *cam);
|
||||
struct camera_buffer buf_desc[CAMERA_MAX_BUFFERS];
|
||||
int buf_count;
|
||||
|
||||
/* Stream state */
|
||||
int s_on;
|
||||
int s_qc;
|
||||
int s_qhead;
|
||||
};
|
||||
|
||||
/* Initialize/destroy a camera. No resources are allocated. */
|
||||
void camera_init(struct camera *c);
|
||||
void camera_destroy(struct camera *c);
|
||||
|
||||
/* Open/close the camera device */
|
||||
int camera_open(struct camera *c, const char *path,
|
||||
int target_w, int target_h,
|
||||
int tr_n, int tr_d);
|
||||
void camera_close(struct camera *c);
|
||||
|
||||
static inline int camera_get_fd(const struct camera *c)
|
||||
{
|
||||
return c->fd;
|
||||
}
|
||||
|
||||
static inline const struct camera_parms *camera_get_parms
|
||||
(const struct camera *c)
|
||||
{
|
||||
return &c->parms;
|
||||
}
|
||||
|
||||
/* Map buffers */
|
||||
int camera_map(struct camera *c, int buf_count);
|
||||
void camera_unmap(struct camera *c);
|
||||
|
||||
static inline int camera_get_buf_count(const struct camera *c)
|
||||
{
|
||||
return c->buf_count;
|
||||
}
|
||||
|
||||
/* Switch streaming on/off */
|
||||
int camera_on(struct camera *c);
|
||||
void camera_off(struct camera *c);
|
||||
|
||||
/* Enqueue/dequeue buffers (count = 0 means enqueue all) */
|
||||
int camera_enqueue_all(struct camera *c);
|
||||
int camera_dequeue_one(struct camera *c);
|
||||
|
||||
/* Fetch the oldest dequeued buffer */
|
||||
static inline const struct camera_buffer *camera_get_head
|
||||
(const struct camera *c)
|
||||
{
|
||||
return &c->buf_desc[(c->s_qhead + c->buf_count - 1) % c->buf_count];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
75
demo/demo.c
75
demo/demo.c
|
@ -33,7 +33,6 @@ static int video_width = 640;
|
|||
static int video_height = 480;
|
||||
static int want_frame_rate = 0;
|
||||
static int want_verbose = 0;
|
||||
static int gain_request = -1;
|
||||
static int printer_timeout = 2;
|
||||
|
||||
static void fat_text(SDL_Surface *screen, int x, int y, const char *text)
|
||||
|
@ -122,23 +121,38 @@ static int main_loop(struct camera *cam, SDL_Surface *screen,
|
|||
|
||||
for (;;) {
|
||||
time_t now = time(NULL);
|
||||
const struct camera_buffer *head;
|
||||
const struct camera_parms *parms = camera_get_parms(cam);
|
||||
|
||||
if (camera_update(cam) < 0)
|
||||
if (camera_dequeue_one(cam) < 0) {
|
||||
perror("camera_dequeue_one");
|
||||
return -1;
|
||||
}
|
||||
|
||||
head = camera_get_head(cam);
|
||||
|
||||
SDL_LockSurface(screen);
|
||||
switch (cam->format) {
|
||||
switch (parms->format) {
|
||||
case CAMERA_FORMAT_MJPEG:
|
||||
mjpeg_decode_rgb32(mj, cam->mem, cam->mem_len,
|
||||
mjpeg_decode_rgb32(mj, head->addr, head->size,
|
||||
screen->pixels, screen->pitch,
|
||||
screen->w, screen->h);
|
||||
break;
|
||||
|
||||
case CAMERA_FORMAT_YUYV:
|
||||
yuyv_to_rgb32(cam->mem, cam->width * 2,
|
||||
cam->width, cam->height,
|
||||
yuyv_to_rgb32(head->addr, parms->width * 2,
|
||||
parms->width, parms->height,
|
||||
screen->pixels, screen->pitch);
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Unknown frame format\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (camera_enqueue_all(cam) < 0) {
|
||||
perror("camera_enqueue_all");
|
||||
return -1;
|
||||
}
|
||||
|
||||
rgb32_to_luma(screen->pixels, screen->pitch,
|
||||
|
@ -177,13 +191,32 @@ static int run_demo(void)
|
|||
struct quirc *qr;
|
||||
struct camera cam;
|
||||
struct mjpeg_decoder mj;
|
||||
const struct camera_parms *parms;
|
||||
SDL_Surface *screen;
|
||||
int ret;
|
||||
|
||||
if (camera_init(&cam, camera_path, video_width, video_height) < 0)
|
||||
return -1;
|
||||
camera_init(&cam);
|
||||
if (camera_open(&cam, camera_path, video_width, video_height,
|
||||
25, 1) < 0) {
|
||||
perror("camera_open");
|
||||
goto fail_qr;
|
||||
}
|
||||
|
||||
camera_set_gain(&cam, gain_request);
|
||||
if (camera_map(&cam, 8) < 0) {
|
||||
perror("camera_map");
|
||||
goto fail_qr;
|
||||
}
|
||||
|
||||
if (camera_on(&cam) < 0) {
|
||||
perror("camera_on");
|
||||
goto fail_qr;
|
||||
}
|
||||
|
||||
if (camera_enqueue_all(&cam) < 0) {
|
||||
perror("camera_enqueue_all");
|
||||
goto fail_qr;
|
||||
}
|
||||
|
||||
parms = camera_get_parms(&cam);
|
||||
|
||||
qr = quirc_new();
|
||||
if (!qr) {
|
||||
|
@ -191,7 +224,7 @@ static int run_demo(void)
|
|||
goto fail_qr;
|
||||
}
|
||||
|
||||
if (quirc_resize(qr, cam.width, cam.height) < 0) {
|
||||
if (quirc_resize(qr, parms->width, parms->height) < 0) {
|
||||
perror("couldn't allocate QR buffer");
|
||||
goto fail_qr_resize;
|
||||
}
|
||||
|
@ -201,7 +234,7 @@ static int run_demo(void)
|
|||
goto fail_sdl_init;
|
||||
}
|
||||
|
||||
screen = SDL_SetVideoMode(cam.width, cam.height, 32,
|
||||
screen = SDL_SetVideoMode(parms->width, parms->height, 32,
|
||||
SDL_SWSURFACE | SDL_DOUBLEBUF);
|
||||
if (!screen) {
|
||||
perror("couldn't init video mode");
|
||||
|
@ -209,24 +242,27 @@ static int run_demo(void)
|
|||
}
|
||||
|
||||
mjpeg_init(&mj);
|
||||
ret = main_loop(&cam, screen, qr, &mj);
|
||||
if (main_loop(&cam, screen, qr, &mj) < 0)
|
||||
goto fail_main_loop;
|
||||
mjpeg_free(&mj);
|
||||
|
||||
SDL_Quit();
|
||||
quirc_destroy(qr);
|
||||
camera_free(&cam);
|
||||
camera_destroy(&cam);
|
||||
|
||||
return 0;
|
||||
|
||||
fail_main_loop:
|
||||
mjpeg_free(&mj);
|
||||
fail_video_mode:
|
||||
SDL_Quit();
|
||||
fail_qr_resize:
|
||||
fail_sdl_init:
|
||||
quirc_destroy(qr);
|
||||
fail_qr:
|
||||
camera_free(&cam);
|
||||
camera_destroy(&cam);
|
||||
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void usage(const char *progname)
|
||||
|
@ -237,7 +273,6 @@ static void usage(const char *progname)
|
|||
" -v Show extra data for detected codes.\n"
|
||||
" -d <device> Specify camera device path.\n"
|
||||
" -s <WxH> Specify video dimensions.\n"
|
||||
" -g <value> Set camera gain.\n"
|
||||
" -p <timeout> Set printer timeout (seconds).\n"
|
||||
" --help Show this information.\n"
|
||||
" --version Show library version information.\n",
|
||||
|
@ -254,7 +289,7 @@ int main(int argc, char **argv)
|
|||
int opt;
|
||||
|
||||
printf("quirc demo\n");
|
||||
printf("Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>\n");
|
||||
printf("Copyright (C) 2010-2014 Daniel Beer <dlbeer@gmail.com>\n");
|
||||
printf("\n");
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "d:s:fvg:p:",
|
||||
|
@ -289,10 +324,6 @@ int main(int argc, char **argv)
|
|||
camera_path = optarg;
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
gain_request = atoi(optarg);
|
||||
break;
|
||||
|
||||
case '?':
|
||||
fprintf(stderr, "Try --help for usage information\n");
|
||||
return -1;
|
||||
|
|
|
@ -32,7 +32,6 @@ static const char *camera_path = "/dev/video0";
|
|||
static int video_width = 640;
|
||||
static int video_height = 480;
|
||||
static int want_verbose = 0;
|
||||
static int gain_request = -1;
|
||||
static int printer_timeout = 2;
|
||||
|
||||
static int main_loop(struct camera *cam,
|
||||
|
@ -46,19 +45,34 @@ static int main_loop(struct camera *cam,
|
|||
int w, h;
|
||||
int i, count;
|
||||
uint8_t *buf = quirc_begin(q, &w, &h);
|
||||
const struct camera_buffer *head;
|
||||
const struct camera_parms *parms = camera_get_parms(cam);
|
||||
|
||||
if (camera_update(cam) < 0)
|
||||
if (camera_dequeue_one(cam) < 0) {
|
||||
perror("camera_dequeue_one");
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (cam->format) {
|
||||
head = camera_get_head(cam);
|
||||
|
||||
switch (parms->format) {
|
||||
case CAMERA_FORMAT_MJPEG:
|
||||
mjpeg_decode_gray(mj, cam->mem, cam->mem_len,
|
||||
mjpeg_decode_gray(mj, head->addr, head->size,
|
||||
buf, w, w, h);
|
||||
break;
|
||||
|
||||
case CAMERA_FORMAT_YUYV:
|
||||
yuyv_to_luma(cam->mem, w * 2, w, h, buf, w);
|
||||
yuyv_to_luma(head->addr, w * 2, w, h, buf, w);
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Unknown frame format\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (camera_enqueue_all(cam) < 0) {
|
||||
perror("camera_enqueue_all");
|
||||
return -1;
|
||||
}
|
||||
|
||||
quirc_end(q);
|
||||
|
@ -80,12 +94,31 @@ static int run_scanner(void)
|
|||
struct quirc *qr;
|
||||
struct camera cam;
|
||||
struct mjpeg_decoder mj;
|
||||
int ret;
|
||||
const struct camera_parms *parms;
|
||||
|
||||
if (camera_init(&cam, camera_path, video_width, video_height) < 0)
|
||||
return -1;
|
||||
camera_init(&cam);
|
||||
if (camera_open(&cam, camera_path, video_width, video_height,
|
||||
25, 1) < 0) {
|
||||
perror("camera_open");
|
||||
goto fail_qr;
|
||||
}
|
||||
|
||||
camera_set_gain(&cam, gain_request);
|
||||
if (camera_map(&cam, 8) < 0) {
|
||||
perror("camera_map");
|
||||
goto fail_qr;
|
||||
}
|
||||
|
||||
if (camera_on(&cam) < 0) {
|
||||
perror("camera_on");
|
||||
goto fail_qr;
|
||||
}
|
||||
|
||||
if (camera_enqueue_all(&cam) < 0) {
|
||||
perror("camera_enqueue_all");
|
||||
goto fail_qr;
|
||||
}
|
||||
|
||||
parms = camera_get_parms(&cam);
|
||||
|
||||
qr = quirc_new();
|
||||
if (!qr) {
|
||||
|
@ -93,26 +126,29 @@ static int run_scanner(void)
|
|||
goto fail_qr;
|
||||
}
|
||||
|
||||
if (quirc_resize(qr, cam.width, cam.height) < 0) {
|
||||
if (quirc_resize(qr, parms->width, parms->height) < 0) {
|
||||
perror("couldn't allocate QR buffer");
|
||||
goto fail_qr_resize;
|
||||
}
|
||||
|
||||
mjpeg_init(&mj);
|
||||
ret = main_loop(&cam, qr, &mj);
|
||||
if (main_loop(&cam, qr, &mj) < 0)
|
||||
goto fail_main_loop;
|
||||
mjpeg_free(&mj);
|
||||
|
||||
quirc_destroy(qr);
|
||||
camera_free(&cam);
|
||||
camera_destroy(&cam);
|
||||
|
||||
return 0;
|
||||
|
||||
fail_main_loop:
|
||||
mjpeg_free(&mj);
|
||||
fail_qr_resize:
|
||||
quirc_destroy(qr);
|
||||
fail_qr:
|
||||
camera_free(&cam);
|
||||
camera_destroy(&cam);
|
||||
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void usage(const char *progname)
|
||||
|
@ -122,7 +158,6 @@ static void usage(const char *progname)
|
|||
" -v Show extra data for detected codes.\n"
|
||||
" -d <device> Specify camera device path.\n"
|
||||
" -s <WxH> Specify video dimensions.\n"
|
||||
" -g <value> Set camera gain.\n"
|
||||
" -p <timeout> Set printer timeout (seconds).\n"
|
||||
" --help Show this information.\n"
|
||||
" --version Show library version information.\n",
|
||||
|
@ -170,10 +205,6 @@ int main(int argc, char **argv)
|
|||
camera_path = optarg;
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
gain_request = atoi(optarg);
|
||||
break;
|
||||
|
||||
case '?':
|
||||
fprintf(stderr, "Try --help for usage information\n");
|
||||
return -1;
|
||||
|
|
Loading…
Reference in a new issue