/*------------------------------------------------------------------------ * Copyright 2007-2010 (c) Jeff Brown * * 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 "window.h" #include "image.h" #ifdef HAVE_DBUS #include #endif 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)); } #ifdef HAVE_DBUS static int dict_add_property (DBusMessageIter *property, const char *key, const char *value) { DBusMessageIter dict_entry, dict_val; DBusError err; dbus_error_init(&err); dbus_message_iter_open_container(property, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); if (!dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &key)){ fprintf(stderr, "Key Error\n"); dbus_message_iter_close_container(property, &dict_entry); goto error; } dbus_message_iter_open_container(&dict_entry, DBUS_TYPE_VARIANT, DBUS_TYPE_STRING_AS_STRING, &dict_val); if (!dbus_message_iter_append_basic(&dict_val, DBUS_TYPE_STRING, &value)){ fprintf(stderr, "Value Error\n"); dbus_message_iter_close_container(&dict_entry, &dict_val); dbus_message_iter_close_container(property, &dict_entry); goto error; } dbus_message_iter_close_container(&dict_entry, &dict_val); dbus_message_iter_close_container(property, &dict_entry); return(1); error: if (dbus_error_is_set(&err)) { fprintf(stderr, "Name Error (%s)\n", err.message); dbus_error_free(&err); } return(0); } static void zbar_send_dbus(int type, const char* sigvalue) { DBusMessage* msg; DBusMessageIter args, dict; DBusConnection* conn; const char *type_name; DBusError err; int ret; dbus_uint32_t serial = 0; // initialise the error value dbus_error_init(&err); // connect to the DBUS system bus, and check for errors conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err); if (dbus_error_is_set(&err)) { fprintf(stderr, "Connection Error (%s)\n", err.message); dbus_error_free(&err); } if (NULL == conn) { fprintf(stderr, "Connection Null\n"); return; } // register our name on the bus, and check for errors ret = dbus_bus_request_name(conn, "org.linuxtv.Zbar", DBUS_NAME_FLAG_REPLACE_EXISTING, &err); if (dbus_error_is_set(&err)) { fprintf(stderr, "Name Error (%s)\n", err.message); dbus_error_free(&err); } if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret) { return; } // create a signal & check for errors msg = dbus_message_new_signal("/org/linuxtv/Zbar1/Code", // object name of the signal "org.linuxtv.Zbar1.Code", // interface name of the signal "Code"); // name of the signal if (NULL == msg) { fprintf(stderr, "Message Null\n"); return; } // append arguments onto signal dbus_message_iter_init_append(msg, &args); if (!dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{sv}", &dict)) { fprintf(stderr, "Out Of Dict Container Memory!\n"); dbus_message_unref(msg); return; } type_name = zbar_get_symbol_name(type); if (!dict_add_property(&dict, "Type", type_name)) { fprintf(stderr, "Out Of Property Memory!\n"); dbus_message_unref(msg); return; } if (!dict_add_property(&dict, "Data", sigvalue)) { fprintf(stderr, "Out Of Property Memory!\n"); dbus_message_unref(msg); return; } dbus_message_iter_close_container(&args, &dict); // send the message and flush the connection if (!dbus_connection_send(conn, msg, &serial)) { fprintf(stderr, "Out Of Memory!\n"); dbus_message_unref(msg); return; } dbus_connection_flush(conn); dbus_bus_release_name(conn, "org.linuxtv.Zbar", &err); if (dbus_error_is_set(&err)) { fprintf(stderr, "Name Release Error (%s)\n", err.message); dbus_error_free(&err); } // free the message dbus_message_unref(msg); } static void zbar_send_code_via_dbus (zbar_image_t *img) { const zbar_symbol_t *sym = zbar_image_first_symbol(img); assert(sym); for(; sym; sym = zbar_symbol_next(sym)) { if(zbar_symbol_get_count(sym)) continue; zbar_symbol_type_t type = zbar_symbol_get_type(sym); if(type == ZBAR_PARTIAL) continue; zbar_send_dbus(type, zbar_symbol_get_data(sym)); } } #endif /* API lock is already held */ int _zbar_process_image (zbar_processor_t *proc, zbar_image_t *img) { uint32_t force_fmt = proc->force_output; if(img) { if(proc->dumping) { zbar_image_write(proc->window->image, "zbar"); proc->dumping = 0; } uint32_t 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... */ zbar_image_t *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); int 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); #ifdef HAVE_DBUS if(proc->is_dbus_enabled) zbar_send_code_via_dbus(img); #endif } 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 */ int 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) { /* 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); zbar_image_t *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) { 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"); int 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) { 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); proc_waiter_t *w, *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) { 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; } int 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 */ int 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 */ int 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) { zbar_processor_t *ncproc = (zbar_processor_t*)proc; _zbar_mutex_lock(&ncproc->mutex); void *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) { proc_enter(proc); int 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) { proc_enter(proc); int value_before, value_after; 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); int 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) { proc_enter(proc); int 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) { proc_enter(proc); int visible = proc->window && proc->visible; proc_leave(proc); return(visible); } int zbar_processor_set_visible (zbar_processor_t *proc, int visible) { proc_enter(proc); _zbar_mutex_unlock(&proc->mutex); int rc = 0; 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) { zbar_processor_t *ncproc = (zbar_processor_t*)proc; proc_enter(ncproc); const zbar_symbol_set_t *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) { proc_enter(proc); _zbar_mutex_unlock(&proc->mutex); int rc = -1; 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) { proc_enter(proc); int rc; 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) { proc_enter(proc); int streaming = proc->streaming; _zbar_mutex_unlock(&proc->mutex); int 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; } zbar_timer_t timer; 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) { proc_enter(proc); _zbar_mutex_unlock(&proc->mutex); int rc = 0; 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); 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); } #ifdef HAVE_DBUS int zbar_processor_request_dbus (zbar_processor_t *proc, int req_dbus_enabled) { proc_enter(proc); proc->is_dbus_enabled = req_dbus_enabled; proc_leave(proc); return(0); } #endif