diff options
Diffstat (limited to 'zbar/processor.c')
-rw-r--r-- | zbar/processor.c | 727 |
1 files changed, 727 insertions, 0 deletions
diff --git a/zbar/processor.c b/zbar/processor.c new file mode 100644 index 0000000..cecd182 --- /dev/null +++ b/zbar/processor.c @@ -0,0 +1,727 @@ +/*------------------------------------------------------------------------ + * 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 "processor.h" +#include "image.h" +#include "img_scanner.h" +#include "window.h" + +static inline int proc_enter(zbar_processor_t *proc) +{ + _zbar_mutex_lock(&proc->mutex); + return (_zbar_processor_lock(proc)); +} + +static inline int proc_leave(zbar_processor_t *proc) +{ + int rc = _zbar_processor_unlock(proc, 0); + _zbar_mutex_unlock(&proc->mutex); + return (rc); +} + +static inline int proc_open(zbar_processor_t *proc) +{ + /* arbitrary default */ + int width = 640, height = 480; + if (proc->video) { + width = zbar_video_get_width(proc->video); + height = zbar_video_get_height(proc->video); + } + return (_zbar_processor_open(proc, "zbar barcode reader", width, height)); +} + +/* API lock is already held */ +int _zbar_process_image(zbar_processor_t *proc, zbar_image_t *img) +{ + int rc; + uint32_t force_fmt = proc->force_output; + if (img) { + uint32_t format; + zbar_image_t *tmp; + int nsyms; + if (proc->dumping) { + zbar_image_write(proc->window->image, "zbar"); + proc->dumping = 0; + } + + format = zbar_image_get_format(img); + zprintf(16, "processing: %.4s(%08" PRIx32 ") %dx%d @%p\n", + (char *)&format, format, zbar_image_get_width(img), + zbar_image_get_height(img), zbar_image_get_data(img)); + + /* FIXME locking all other interfaces while processing is conservative + * but easier for now and we don't expect this to take long... + */ + tmp = zbar_image_convert(img, fourcc('Y', '8', '0', '0')); + if (!tmp) + goto error; + + if (proc->syms) { + zbar_symbol_set_ref(proc->syms, -1); + proc->syms = NULL; + } + zbar_image_scanner_recycle_image(proc->scanner, img); + nsyms = zbar_scan_image(proc->scanner, tmp); + _zbar_image_swap_symbols(img, tmp); + + zbar_image_destroy(tmp); + tmp = NULL; + if (nsyms < 0) + goto error; + + proc->syms = zbar_image_scanner_get_results(proc->scanner); + if (proc->syms) + zbar_symbol_set_ref(proc->syms, 1); + + if (_zbar_verbosity >= 8) { + const zbar_symbol_t *sym = zbar_image_first_symbol(img); + while (sym) { + zbar_symbol_type_t type = zbar_symbol_get_type(sym); + int count = zbar_symbol_get_count(sym); + zprintf(8, "%s: %s (%d pts) (dir=%d) (q=%d) (%s)\n", + zbar_get_symbol_name(type), zbar_symbol_get_data(sym), + zbar_symbol_get_loc_size(sym), + zbar_symbol_get_orientation(sym), + zbar_symbol_get_quality(sym), + (count < 0) ? "uncertain" : + (count > 0) ? "duplicate" : + "new"); + sym = zbar_symbol_next(sym); + } + } + + if (nsyms) { + /* FIXME only call after filtering */ + _zbar_mutex_lock(&proc->mutex); + _zbar_processor_notify(proc, EVENT_OUTPUT); + _zbar_mutex_unlock(&proc->mutex); + if (proc->handler) + proc->handler(img, proc->userdata); + } + + if (force_fmt) { + zbar_symbol_set_t *syms = img->syms; + img = zbar_image_convert(img, force_fmt); + if (!img) + goto error; + img->syms = syms; + zbar_symbol_set_ref(syms, 1); + } + } + + /* display to window if enabled */ + rc = 0; + if (proc->window) { + if ((rc = zbar_window_draw(proc->window, img))) + err_copy(proc, proc->window); + _zbar_processor_invalidate(proc); + } + + if (force_fmt && img) + zbar_image_destroy(img); + return (rc); + +error: + return (err_capture(proc, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__, + "unknown image format")); +} + +int _zbar_processor_handle_input(zbar_processor_t *proc, int input) +{ + int event = EVENT_INPUT; + switch (input) { + case -1: + event |= EVENT_CANCELED; + _zbar_processor_set_visible(proc, 0); + err_capture(proc, SEV_WARNING, ZBAR_ERR_CLOSED, __func__, + "user closed display window"); + break; + + case 'd': + proc->dumping = 1; + return (0); + + case '+': + case '=': + if (proc->window) { + int ovl = zbar_window_get_overlay(proc->window); + zbar_window_set_overlay(proc->window, ovl + 1); + } + break; + + case '-': + if (proc->window) { + int ovl = zbar_window_get_overlay(proc->window); + zbar_window_set_overlay(proc->window, ovl - 1); + } + break; + } + + _zbar_mutex_lock(&proc->mutex); + proc->input = input; + if (input == -1 && proc->visible && proc->streaming) + /* also cancel outstanding output waiters */ + event |= EVENT_OUTPUT; + _zbar_processor_notify(proc, event); + _zbar_mutex_unlock(&proc->mutex); + return (input); +} + +#ifdef ZTHREAD + +static ZTHREAD proc_video_thread(void *arg) +{ + zbar_processor_t *proc = arg; + zbar_thread_t *thread = &proc->video_thread; + + _zbar_mutex_lock(&proc->mutex); + _zbar_thread_init(thread); + zprintf(4, "spawned video thread\n"); + + while (thread->started) { + zbar_image_t *img; + /* wait for video stream to be active */ + while (thread->started && !proc->streaming) + _zbar_event_wait(&thread->notify, &proc->mutex, NULL); + if (!thread->started) + break; + + /* blocking capture image from video */ + _zbar_mutex_unlock(&proc->mutex); + img = zbar_video_next_image(proc->video); + _zbar_mutex_lock(&proc->mutex); + + if (!img && !proc->streaming) + continue; + else if (!img) + /* FIXME could abort streaming and keep running? */ + break; + + /* acquire API lock */ + _zbar_processor_lock(proc); + _zbar_mutex_unlock(&proc->mutex); + + if (thread->started && proc->streaming) + _zbar_process_image(proc, img); + + zbar_image_destroy(img); + + _zbar_mutex_lock(&proc->mutex); + /* release API lock */ + _zbar_processor_unlock(proc, 0); + } + + thread->running = 0; + _zbar_event_trigger(&thread->activity); + _zbar_mutex_unlock(&proc->mutex); + return (0); +} + +static ZTHREAD proc_input_thread(void *arg) +{ + int rc = 0; + zbar_processor_t *proc = arg; + zbar_thread_t *thread = &proc->input_thread; + if (proc->window && proc_open(proc)) + goto done; + + _zbar_mutex_lock(&proc->mutex); + thread->running = 1; + _zbar_event_trigger(&thread->activity); + zprintf(4, "spawned input thread\n"); + + rc = 0; + while (thread->started && rc >= 0) { + _zbar_mutex_unlock(&proc->mutex); + rc = _zbar_processor_input_wait(proc, &thread->notify, -1); + _zbar_mutex_lock(&proc->mutex); + } + + _zbar_mutex_unlock(&proc->mutex); + _zbar_processor_close(proc); + _zbar_mutex_lock(&proc->mutex); + +done: + thread->running = 0; + _zbar_event_trigger(&thread->activity); + _zbar_mutex_unlock(&proc->mutex); + return (0); +} + +#endif + +zbar_processor_t *zbar_processor_create(int threaded) +{ + zbar_processor_t *proc = calloc(1, sizeof(zbar_processor_t)); + if (!proc) + return (NULL); + err_init(&proc->err, ZBAR_MOD_PROCESSOR); + + proc->scanner = zbar_image_scanner_create(); + if (!proc->scanner) { + free(proc); + return (NULL); + } + + proc->threaded = !_zbar_mutex_init(&proc->mutex) && threaded; + _zbar_processor_init(proc); + return (proc); +} + +void zbar_processor_destroy(zbar_processor_t *proc) +{ + proc_waiter_t *w, *next; + + zbar_processor_init(proc, NULL, 0); + + if (proc->syms) { + zbar_symbol_set_ref(proc->syms, -1); + proc->syms = NULL; + } + if (proc->scanner) { + zbar_image_scanner_destroy(proc->scanner); + proc->scanner = NULL; + } + + _zbar_mutex_destroy(&proc->mutex); + _zbar_processor_cleanup(proc); + + assert(!proc->wait_head); + assert(!proc->wait_tail); + assert(!proc->wait_next); + + for (w = proc->free_waiter; w; w = next) { + next = w->next; + _zbar_event_destroy(&w->notify); + free(w); + } + + err_cleanup(&proc->err); + free(proc); +} + +int zbar_processor_init(zbar_processor_t *proc, const char *dev, + int enable_display) +{ + int rc = 0; + int video_threaded, input_threaded; + + if (proc->video) + zbar_processor_set_active(proc, 0); + + if (proc->window && !proc->input_thread.started) + _zbar_processor_close(proc); + + _zbar_mutex_lock(&proc->mutex); + _zbar_thread_stop(&proc->input_thread, &proc->mutex); + _zbar_thread_stop(&proc->video_thread, &proc->mutex); + + _zbar_processor_lock(proc); + _zbar_mutex_unlock(&proc->mutex); + + if (proc->window) { + zbar_window_destroy(proc->window); + proc->window = NULL; + } + + rc = 0; + if (proc->video) { + zbar_video_destroy(proc->video); + proc->video = NULL; + } + + if (!dev && !enable_display) + /* nothing to do */ + goto done; + + if (enable_display) { + proc->window = zbar_window_create(); + if (!proc->window) { + rc = err_capture(proc, SEV_FATAL, ZBAR_ERR_NOMEM, __func__, + "allocating window resources"); + goto done; + } + } + + if (dev) { + proc->video = zbar_video_create(); + if (!proc->video) { + rc = err_capture(proc, SEV_FATAL, ZBAR_ERR_NOMEM, __func__, + "allocating video resources"); + goto done; + } + if (proc->req_width || proc->req_height) + zbar_video_request_size(proc->video, proc->req_width, + proc->req_height); + if (proc->req_intf) + zbar_video_request_interface(proc->video, proc->req_intf); + if ((proc->req_iomode && + zbar_video_request_iomode(proc->video, proc->req_iomode)) || + zbar_video_open(proc->video, dev)) { + rc = err_copy(proc, proc->video); + goto done; + } + } + + /* spawn blocking video thread */ + video_threaded = + (proc->threaded && proc->video && zbar_video_get_fd(proc->video) < 0); + if (video_threaded && + _zbar_thread_start(&proc->video_thread, proc_video_thread, proc, + &proc->mutex)) { + rc = err_capture(proc, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "spawning video thread"); + goto done; + } + + /* spawn input monitor thread */ + input_threaded = + (proc->threaded && (proc->window || (proc->video && !video_threaded))); + if (input_threaded && + _zbar_thread_start(&proc->input_thread, proc_input_thread, proc, + &proc->mutex)) { + rc = err_capture(proc, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "spawning input thread"); + goto done; + } + + if (proc->window && !input_threaded && (rc = proc_open(proc))) + goto done; + + if (proc->video && proc->force_input) { + if (zbar_video_init(proc->video, proc->force_input)) + rc = err_copy(proc, proc->video); + } else if (proc->video) { + int retry = -1; + if (proc->window) { + retry = zbar_negotiate_format(proc->video, proc->window); + if (retry) + fprintf(stderr, + "WARNING: no compatible input to output format\n" + "...trying again with output disabled\n"); + } + if (retry) + retry = zbar_negotiate_format(proc->video, NULL); + + if (retry) { + zprintf(1, "ERROR: no compatible %s format\n", + (proc->video) ? "video input" : "window output"); + rc = err_capture(proc, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__, + "no compatible image format"); + } + } + +done: + _zbar_mutex_lock(&proc->mutex); + proc_leave(proc); + return (rc); +} + +zbar_image_data_handler_t * +zbar_processor_set_data_handler(zbar_processor_t *proc, + zbar_image_data_handler_t *handler, + const void *userdata) +{ + zbar_image_data_handler_t *result = NULL; + proc_enter(proc); + + result = proc->handler; + proc->handler = handler; + proc->userdata = userdata; + + proc_leave(proc); + return (result); +} + +void zbar_processor_set_userdata(zbar_processor_t *proc, void *userdata) +{ + _zbar_mutex_lock(&proc->mutex); + proc->userdata = userdata; + _zbar_mutex_unlock(&proc->mutex); +} + +void *zbar_processor_get_userdata(const zbar_processor_t *proc) +{ + void *userdata; + zbar_processor_t *ncproc = (zbar_processor_t *)proc; + _zbar_mutex_lock(&ncproc->mutex); + userdata = (void *)ncproc->userdata; + _zbar_mutex_unlock(&ncproc->mutex); + return (userdata); +} + +int zbar_processor_set_config(zbar_processor_t *proc, zbar_symbol_type_t sym, + zbar_config_t cfg, int val) +{ + int rc; + proc_enter(proc); + rc = zbar_image_scanner_set_config(proc->scanner, sym, cfg, val); + proc_leave(proc); + return (rc); +} + +int zbar_processor_set_control(zbar_processor_t *proc, const char *control_name, + int value) +{ + int rc; + int value_before, value_after; + proc_enter(proc); + if (_zbar_verbosity >= 4) + if (zbar_video_get_control(proc->video, control_name, &value_before) == + 0) + zprintf(0, "value of %s before a set: %d\n", control_name, + value_before); + rc = zbar_video_set_control(proc->video, control_name, value); + if (_zbar_verbosity >= 4) + if (zbar_video_get_control(proc->video, control_name, &value_after) == + 0) + zprintf(0, "value of %s after a set: %d\n", control_name, + value_after); + proc_leave(proc); + return (rc); +} + +int zbar_processor_get_control(zbar_processor_t *proc, const char *control_name, + int *value) +{ + int rc; + proc_enter(proc); + rc = zbar_video_get_control(proc->video, control_name, value); + proc_leave(proc); + return (rc); +} + +int zbar_processor_request_size(zbar_processor_t *proc, unsigned width, + unsigned height) +{ + proc_enter(proc); + proc->req_width = width; + proc->req_height = height; + proc_leave(proc); + return (0); +} + +int zbar_processor_request_interface(zbar_processor_t *proc, int ver) +{ + proc_enter(proc); + proc->req_intf = ver; + proc_leave(proc); + return (0); +} + +int zbar_processor_request_iomode(zbar_processor_t *proc, int iomode) +{ + proc_enter(proc); + proc->req_iomode = iomode; + proc_leave(proc); + return (0); +} + +int zbar_processor_force_format(zbar_processor_t *proc, unsigned long input, + unsigned long output) +{ + proc_enter(proc); + proc->force_input = input; + proc->force_output = output; + proc_leave(proc); + return (0); +} + +int zbar_processor_is_visible(zbar_processor_t *proc) +{ + int visible; + proc_enter(proc); + visible = proc->window && proc->visible; + proc_leave(proc); + return (visible); +} + +int zbar_processor_set_visible(zbar_processor_t *proc, int visible) +{ + int rc = 0; + proc_enter(proc); + _zbar_mutex_unlock(&proc->mutex); + + if (proc->window) { + if (proc->video) + rc = _zbar_processor_set_size(proc, + zbar_video_get_width(proc->video), + zbar_video_get_height(proc->video)); + if (!rc) + rc = _zbar_processor_set_visible(proc, visible); + + if (!rc) + proc->visible = (visible != 0); + } else if (visible) + rc = err_capture(proc, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "processor display window not initialized"); + + _zbar_mutex_lock(&proc->mutex); + proc_leave(proc); + return (rc); +} + +const zbar_symbol_set_t * +zbar_processor_get_results(const zbar_processor_t *proc) +{ + const zbar_symbol_set_t *syms; + zbar_processor_t *ncproc = (zbar_processor_t *)proc; + proc_enter(ncproc); + syms = proc->syms; + if (syms) + zbar_symbol_set_ref(syms, 1); + proc_leave(ncproc); + return (syms); +} + +int zbar_processor_user_wait(zbar_processor_t *proc, int timeout) +{ + int rc = -1; + + proc_enter(proc); + _zbar_mutex_unlock(&proc->mutex); + + if (proc->visible || proc->streaming || timeout >= 0) { + zbar_timer_t timer; + rc = _zbar_processor_wait(proc, EVENT_INPUT, + _zbar_timer_init(&timer, timeout)); + } + + if (!proc->visible) + rc = err_capture(proc, SEV_WARNING, ZBAR_ERR_CLOSED, __func__, + "display window not available for input"); + + if (rc > 0) + rc = proc->input; + + _zbar_mutex_lock(&proc->mutex); + proc_leave(proc); + return (rc); +} + +int zbar_processor_set_active(zbar_processor_t *proc, int active) +{ + int rc; + proc_enter(proc); + + if (!proc->video) { + rc = err_capture(proc, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "video input not initialized"); + goto done; + } + _zbar_mutex_unlock(&proc->mutex); + + zbar_image_scanner_enable_cache(proc->scanner, active); + + rc = zbar_video_enable(proc->video, active); + if (!rc) { + _zbar_mutex_lock(&proc->mutex); + proc->streaming = active; + _zbar_mutex_unlock(&proc->mutex); + rc = _zbar_processor_enable(proc); + } else + err_copy(proc, proc->video); + + if (!proc->streaming && proc->window) { + if (zbar_window_draw(proc->window, NULL) && !rc) + rc = err_copy(proc, proc->window); + _zbar_processor_invalidate(proc); + } + + _zbar_mutex_lock(&proc->mutex); + if (proc->video_thread.started) + _zbar_event_trigger(&proc->video_thread.notify); + +done: + proc_leave(proc); + return (rc); +} + +int zbar_process_one(zbar_processor_t *proc, int timeout) +{ + int streaming, rc; + zbar_timer_t timer; + + proc_enter(proc); + streaming = proc->streaming; + _zbar_mutex_unlock(&proc->mutex); + + rc = 0; + if (!proc->video) { + rc = err_capture(proc, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "video input not initialized"); + goto done; + } + + if (!streaming) { + rc = zbar_processor_set_active(proc, 1); + if (rc) + goto done; + } + + rc = _zbar_processor_wait(proc, EVENT_OUTPUT, + _zbar_timer_init(&timer, timeout)); + + if (!streaming && zbar_processor_set_active(proc, 0)) + rc = -1; + +done: + _zbar_mutex_lock(&proc->mutex); + proc_leave(proc); + return (rc); +} + +int zbar_process_image(zbar_processor_t *proc, zbar_image_t *img) +{ + int rc = 0; + + proc_enter(proc); + _zbar_mutex_unlock(&proc->mutex); + + if (img && proc->window) + rc = _zbar_processor_set_size(proc, zbar_image_get_width(img), + zbar_image_get_height(img)); + if (!rc) { + zbar_image_scanner_enable_cache(proc->scanner, 0); + zbar_image_scanner_request_dbus(proc->scanner, proc->is_dbus_enabled); + rc = _zbar_process_image(proc, img); + if (proc->streaming) + zbar_image_scanner_enable_cache(proc->scanner, 1); + } + + _zbar_mutex_lock(&proc->mutex); + proc_leave(proc); + return (rc); +} + +int zbar_processor_request_dbus(zbar_processor_t *proc, int req_dbus_enabled) +{ +#ifdef HAVE_DBUS + proc_enter(proc); + proc->is_dbus_enabled = req_dbus_enabled; + proc_leave(proc); + return (0); +#else + return (1); +#endif +} |