diff options
Diffstat (limited to 'zbar/video.c')
-rw-r--r-- | zbar/video.c | 452 |
1 files changed, 452 insertions, 0 deletions
diff --git a/zbar/video.c b/zbar/video.c new file mode 100644 index 0000000..664b037 --- /dev/null +++ b/zbar/video.c @@ -0,0 +1,452 @@ +/*------------------------------------------------------------------------ + * 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; +} + +struct video_resolution_s *zbar_video_get_resolutions(const zbar_video_t *vdo, + int index) +{ + int i = 0; + struct video_resolution_s *p = vdo->res; + + while (i != index) { + if (!p->width || !p->height) + return NULL; + i++; + p++; + } + + if (!p->width || !p->height) + return NULL; + + return p; +} |