441 lines
12 KiB
C
441 lines
12 KiB
C
/*------------------------------------------------------------------------
|
|
* Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net>
|
|
*
|
|
* This file is part of the ZBar Bar Code Reader.
|
|
*
|
|
* The ZBar Bar Code Reader is free software; you can redistribute it
|
|
* and/or modify it under the terms of the GNU Lesser Public License as
|
|
* published by the Free Software Foundation; either version 2.1 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* The ZBar Bar Code Reader is distributed in the hope that it will be
|
|
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser Public License
|
|
* along with the ZBar Bar Code Reader; if not, write to the Free
|
|
* Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301 USA
|
|
*
|
|
* http://sourceforge.net/projects/zbar
|
|
*------------------------------------------------------------------------*/
|
|
|
|
#include "video.h"
|
|
#include "image.h"
|
|
|
|
|
|
#ifdef HAVE_LIBJPEG
|
|
extern struct jpeg_decompress_struct *_zbar_jpeg_decomp_create(void);
|
|
extern void _zbar_jpeg_decomp_destroy(struct jpeg_decompress_struct *cinfo);
|
|
#endif
|
|
|
|
static void _zbar_video_recycle_image (zbar_image_t *img)
|
|
{
|
|
zbar_video_t *vdo = img->src;
|
|
assert(vdo);
|
|
assert(img->srcidx >= 0);
|
|
video_lock(vdo);
|
|
if(vdo->images[img->srcidx] != img)
|
|
vdo->images[img->srcidx] = img;
|
|
if(vdo->active)
|
|
vdo->nq(vdo, img);
|
|
else
|
|
video_unlock(vdo);
|
|
}
|
|
|
|
static void _zbar_video_recycle_shadow (zbar_image_t *img)
|
|
{
|
|
zbar_video_t *vdo = img->src;
|
|
assert(vdo);
|
|
assert(img->srcidx == -1);
|
|
video_lock(vdo);
|
|
img->next = vdo->shadow_image;
|
|
vdo->shadow_image = img;
|
|
video_unlock(vdo);
|
|
}
|
|
|
|
zbar_video_t *zbar_video_create ()
|
|
{
|
|
zbar_video_t *vdo = calloc(1, sizeof(zbar_video_t));
|
|
int i;
|
|
if(!vdo)
|
|
return(NULL);
|
|
err_init(&vdo->err, ZBAR_MOD_VIDEO);
|
|
vdo->fd = -1;
|
|
|
|
(void)_zbar_mutex_init(&vdo->qlock);
|
|
|
|
/* pre-allocate images */
|
|
vdo->num_images = ZBAR_VIDEO_IMAGES_MAX;
|
|
vdo->images = calloc(ZBAR_VIDEO_IMAGES_MAX, sizeof(zbar_image_t*));
|
|
if(!vdo->images) {
|
|
zbar_video_destroy(vdo);
|
|
return(NULL);
|
|
}
|
|
|
|
for(i = 0; i < ZBAR_VIDEO_IMAGES_MAX; i++) {
|
|
zbar_image_t *img = vdo->images[i] = zbar_image_create();
|
|
if(!img) {
|
|
zbar_video_destroy(vdo);
|
|
return(NULL);
|
|
}
|
|
img->refcnt = 0;
|
|
img->cleanup = _zbar_video_recycle_image;
|
|
img->srcidx = i;
|
|
img->src = vdo;
|
|
}
|
|
|
|
return(vdo);
|
|
}
|
|
|
|
void zbar_video_destroy (zbar_video_t *vdo)
|
|
{
|
|
if(vdo->intf != VIDEO_INVALID)
|
|
zbar_video_open(vdo, NULL);
|
|
if(vdo->images) {
|
|
int i;
|
|
for(i = 0; i < ZBAR_VIDEO_IMAGES_MAX; i++)
|
|
if(vdo->images[i])
|
|
_zbar_image_free(vdo->images[i]);
|
|
free(vdo->images);
|
|
}
|
|
while(vdo->shadow_image) {
|
|
zbar_image_t *img = vdo->shadow_image;
|
|
vdo->shadow_image = img->next;
|
|
free((void*)img->data);
|
|
img->data = NULL;
|
|
free(img);
|
|
}
|
|
if(vdo->buf)
|
|
free(vdo->buf);
|
|
if(vdo->formats)
|
|
free(vdo->formats);
|
|
if(vdo->emu_formats)
|
|
free(vdo->emu_formats);
|
|
|
|
if(vdo->free)
|
|
vdo->free(vdo);
|
|
|
|
err_cleanup(&vdo->err);
|
|
_zbar_mutex_destroy(&vdo->qlock);
|
|
|
|
#ifdef HAVE_LIBJPEG
|
|
if(vdo->jpeg_img) {
|
|
zbar_image_destroy(vdo->jpeg_img);
|
|
vdo->jpeg_img = NULL;
|
|
}
|
|
if(vdo->jpeg) {
|
|
_zbar_jpeg_decomp_destroy(vdo->jpeg);
|
|
vdo->jpeg = NULL;
|
|
}
|
|
#endif
|
|
free(vdo);
|
|
}
|
|
|
|
int zbar_video_open (zbar_video_t *vdo,
|
|
const char *dev)
|
|
{
|
|
char *ldev = NULL;
|
|
int rc;
|
|
zbar_video_enable(vdo, 0);
|
|
video_lock(vdo);
|
|
if(vdo->intf != VIDEO_INVALID) {
|
|
if(vdo->cleanup) {
|
|
vdo->cleanup(vdo);
|
|
vdo->cleanup = NULL;
|
|
}
|
|
zprintf(1, "closed camera (fd=%d)\n", vdo->fd);
|
|
vdo->intf = VIDEO_INVALID;
|
|
}
|
|
video_unlock(vdo);
|
|
|
|
if(!dev)
|
|
return(0);
|
|
|
|
if((unsigned char)dev[0] < 0x10) {
|
|
/* default linux device, overloaded for other platforms */
|
|
int id = dev[0];
|
|
dev = ldev = strdup("/dev/video0");
|
|
ldev[10] = '0' + id;
|
|
}
|
|
|
|
rc = _zbar_video_open(vdo, dev);
|
|
|
|
if(ldev)
|
|
free(ldev);
|
|
return(rc);
|
|
}
|
|
|
|
int zbar_video_get_fd (const zbar_video_t *vdo)
|
|
{
|
|
if(vdo->intf == VIDEO_INVALID)
|
|
return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__,
|
|
"video device not opened"));
|
|
if(vdo->intf != VIDEO_V4L2)
|
|
return(err_capture(vdo, SEV_WARNING, ZBAR_ERR_UNSUPPORTED, __func__,
|
|
"video driver does not support polling"));
|
|
return(vdo->fd);
|
|
}
|
|
|
|
int zbar_video_request_size (zbar_video_t *vdo,
|
|
unsigned width,
|
|
unsigned height)
|
|
{
|
|
if(vdo->initialized)
|
|
/* FIXME re-init different format? */
|
|
return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__,
|
|
"already initialized, unable to resize"));
|
|
|
|
vdo->width = width;
|
|
vdo->height = height;
|
|
zprintf(1, "request size: %d x %d\n", width, height);
|
|
return(0);
|
|
}
|
|
|
|
int zbar_video_request_interface (zbar_video_t *vdo,
|
|
int ver)
|
|
{
|
|
if(vdo->intf != VIDEO_INVALID)
|
|
return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__,
|
|
"device already opened, unable to change interface"));
|
|
vdo->intf = (video_interface_t)ver;
|
|
zprintf(1, "request interface version %d\n", vdo->intf);
|
|
return(0);
|
|
}
|
|
|
|
int zbar_video_request_iomode (zbar_video_t *vdo,
|
|
int iomode)
|
|
{
|
|
if(vdo->intf != VIDEO_INVALID)
|
|
return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__,
|
|
"device already opened, unable to change iomode"));
|
|
if(iomode < 0 || iomode > VIDEO_USERPTR)
|
|
return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__,
|
|
"invalid iomode requested"));
|
|
vdo->iomode = iomode;
|
|
return(0);
|
|
}
|
|
|
|
int zbar_video_get_width (const zbar_video_t *vdo)
|
|
{
|
|
return(vdo->width);
|
|
}
|
|
|
|
int zbar_video_get_height (const zbar_video_t *vdo)
|
|
{
|
|
return(vdo->height);
|
|
}
|
|
|
|
uint32_t zbar_video_get_format (const zbar_video_t *vdo)
|
|
{
|
|
return(vdo->format);
|
|
}
|
|
|
|
static inline int video_init_images (zbar_video_t *vdo)
|
|
{
|
|
int i;
|
|
assert(vdo->datalen);
|
|
if(vdo->iomode != VIDEO_MMAP) {
|
|
assert(!vdo->buf);
|
|
vdo->buflen = vdo->num_images * vdo->datalen;
|
|
vdo->buf = calloc(1, vdo->buflen);
|
|
if(!vdo->buf)
|
|
return(err_capture(vdo, SEV_FATAL, ZBAR_ERR_NOMEM, __func__,
|
|
"unable to allocate image buffers"));
|
|
zprintf(1, "pre-allocated %d %s buffers size=0x%lx\n", vdo->num_images,
|
|
(vdo->iomode == VIDEO_READWRITE) ? "READ" : "USERPTR",
|
|
vdo->buflen);
|
|
}
|
|
for(i = 0; i < vdo->num_images; i++) {
|
|
zbar_image_t *img = vdo->images[i];
|
|
img->format = vdo->format;
|
|
zbar_image_set_size(img, vdo->width, vdo->height);
|
|
if(vdo->iomode != VIDEO_MMAP) {
|
|
unsigned long offset = i * vdo->datalen;
|
|
img->datalen = vdo->datalen;
|
|
img->data = (uint8_t*)vdo->buf + offset;
|
|
zprintf(2, " [%02d] @%08lx\n", i, offset);
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
int zbar_video_init (zbar_video_t *vdo,
|
|
unsigned long fmt)
|
|
{
|
|
#ifdef HAVE_LIBJPEG
|
|
const zbar_format_def_t *vidfmt;
|
|
#endif
|
|
if(vdo->initialized)
|
|
/* FIXME re-init different format? */
|
|
return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__,
|
|
"already initialized, re-init unimplemented"));
|
|
|
|
if(vdo->init(vdo, fmt))
|
|
return(-1);
|
|
vdo->format = fmt;
|
|
if(video_init_images(vdo))
|
|
return(-1);
|
|
#ifdef HAVE_LIBJPEG
|
|
vidfmt = _zbar_format_lookup(fmt);
|
|
if(vidfmt && vidfmt->group == ZBAR_FMT_JPEG) {
|
|
zbar_image_t *img;
|
|
/* prepare for decoding */
|
|
if(!vdo->jpeg)
|
|
vdo->jpeg = _zbar_jpeg_decomp_create();
|
|
if(vdo->jpeg_img)
|
|
zbar_image_destroy(vdo->jpeg_img);
|
|
|
|
/* create intermediate image for decoder to use*/
|
|
img = vdo->jpeg_img = zbar_image_create();
|
|
img->format = fourcc('Y','8','0','0');
|
|
zbar_image_set_size(img, vdo->width, vdo->height);
|
|
img->datalen = vdo->width * vdo->height;
|
|
}
|
|
#endif
|
|
vdo->initialized = 1;
|
|
return(0);
|
|
}
|
|
|
|
int zbar_video_enable (zbar_video_t *vdo,
|
|
int enable)
|
|
{
|
|
if(vdo->active == enable)
|
|
return(0);
|
|
|
|
if(enable) {
|
|
if(vdo->intf == VIDEO_INVALID)
|
|
return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__,
|
|
"video device not opened"));
|
|
|
|
if(!vdo->initialized &&
|
|
zbar_negotiate_format(vdo, NULL))
|
|
return(-1);
|
|
}
|
|
|
|
if(video_lock(vdo))
|
|
return(-1);
|
|
vdo->active = enable;
|
|
if(enable) {
|
|
/* enqueue all buffers */
|
|
int i;
|
|
for(i = 0; i < vdo->num_images; i++)
|
|
if(vdo->nq(vdo, vdo->images[i]) ||
|
|
((i + 1 < vdo->num_images) && video_lock(vdo)))
|
|
return(-1);
|
|
|
|
return(vdo->start(vdo));
|
|
}
|
|
else {
|
|
int i;
|
|
for(i = 0; i < vdo->num_images; i++)
|
|
vdo->images[i]->next = NULL;
|
|
vdo->nq_image = vdo->dq_image = NULL;
|
|
if(video_unlock(vdo))
|
|
return(-1);
|
|
|
|
return(vdo->stop(vdo));
|
|
}
|
|
}
|
|
|
|
zbar_image_t *zbar_video_next_image (zbar_video_t *vdo)
|
|
{
|
|
unsigned frame;
|
|
zbar_image_t *img;
|
|
|
|
if(video_lock(vdo))
|
|
return(NULL);
|
|
if(!vdo->active) {
|
|
video_unlock(vdo);
|
|
return(NULL);
|
|
}
|
|
|
|
frame = vdo->frame++;
|
|
img = vdo->dq(vdo);
|
|
if(img) {
|
|
img->seq = frame;
|
|
if(vdo->num_images < 2) {
|
|
/* return a *copy* of the video image and immediately recycle
|
|
* the driver's buffer to avoid deadlocking the resources
|
|
*/
|
|
zbar_image_t *tmp = img;
|
|
video_lock(vdo);
|
|
img = vdo->shadow_image;
|
|
vdo->shadow_image = (img) ? img->next : NULL;
|
|
video_unlock(vdo);
|
|
|
|
if(!img) {
|
|
img = zbar_image_create();
|
|
assert(img);
|
|
img->refcnt = 0;
|
|
img->src = vdo;
|
|
/* recycle the shadow images */
|
|
|
|
img->format = vdo->format;
|
|
zbar_image_set_size(img, vdo->width, vdo->height);
|
|
img->datalen = vdo->datalen;
|
|
img->data = malloc(vdo->datalen);
|
|
}
|
|
img->cleanup = _zbar_video_recycle_shadow;
|
|
img->seq = frame;
|
|
memcpy((void*)img->data, tmp->data, img->datalen);
|
|
_zbar_video_recycle_image(tmp);
|
|
}
|
|
else
|
|
img->cleanup = _zbar_video_recycle_image;
|
|
_zbar_image_refcnt(img, 1);
|
|
}
|
|
return(img);
|
|
}
|
|
|
|
/** @brief return if fun unsupported, otherwise continue */
|
|
#define return_if_not_supported(fun, name) \
|
|
{ \
|
|
if(!(fun)) { \
|
|
zprintf(1, "video driver does not implement %s\n", name); \
|
|
return ZBAR_ERR_UNSUPPORTED; \
|
|
} \
|
|
}
|
|
#define return_if_non_zero(a) { int rv=a; if (rv!=0) return(rv); }
|
|
|
|
int zbar_video_set_control (zbar_video_t *vdo,
|
|
const char *control_name,
|
|
int value)
|
|
{
|
|
int loc_value, rv;
|
|
return_if_not_supported(vdo->set_control, "set_control");
|
|
loc_value = value;
|
|
|
|
rv = vdo->set_control(vdo, control_name, &loc_value);
|
|
|
|
if(rv==0)
|
|
zprintf(1, "value of %s set to: %d\n", control_name, loc_value);
|
|
return(rv);
|
|
}
|
|
|
|
int zbar_video_get_control (zbar_video_t *vdo,
|
|
const char *control_name,
|
|
int *value)
|
|
{
|
|
return_if_not_supported(vdo->get_control, "get_control");
|
|
return(vdo->get_control(vdo, control_name, value));
|
|
}
|
|
|
|
struct video_controls_s *zbar_video_get_controls (const zbar_video_t *vdo,
|
|
int index)
|
|
{
|
|
int i = 0;
|
|
struct video_controls_s *p = vdo->controls;
|
|
|
|
while (p && i != index) {
|
|
i++;
|
|
p = p->next;
|
|
}
|
|
|
|
if (!p)
|
|
return NULL;
|
|
|
|
return p;
|
|
}
|