How to Build a Java Barcode Scanner with LiteCam JNI, ZXing, and Dynamsoft Barcode Reader

Java Development Kit (JDK) does not provide a built-in API for camera access. A common workaround is to use OpenCV with Java bindings (such as JavaCV). However, OpenCV is a fairly large library, which may not be suitable for all applications.

In this tutorial, we will demonstrate how to build a Java Camera SDK by wrapping the LiteCam C++ SDK, and then integrate it with ZXing and Dynamsoft Barcode Reader to create a complete barcode scanning solution — from setup to deployment.

What you’ll build: A cross-platform Java barcode scanner application that captures live camera frames via a JNI-wrapped LiteCam SDK and decodes barcodes using either ZXing or Dynamsoft Barcode Reader.

Key Takeaways

  • You can give Java applications native camera access without OpenCV by wrapping a lightweight C++ SDK (LiteCam) through JNI and packaging the native library inside a JAR.
  • Dynamsoft Barcode Reader’s CaptureVisionRouter API decodes multiple barcode formats from raw ImageData in a single call, making it straightforward to drop into any Java frame-capture loop.
  • ZXing provides a zero-cost baseline; Dynamsoft Barcode Reader outperforms it on damaged, low-contrast, and multi-barcode frames.
  • The Maven build packages the native .dll/.so/.dylib alongside Java classes, so end users need no manual library setup.

Common Developer Questions

  • How do I access a webcam from Java without OpenCV?
  • What is the difference between ZXing and Dynamsoft Barcode Reader for real-time scanning accuracy?
  • How do I bundle a native JNI library inside a Maven JAR so it loads automatically?

Demo: Java Barcode Scanner in Action

Prerequisites

System Requirements

  • Java JDK 8+ and Maven 3.6+
  • A camera device (for scanning)
  • Platform dependencies: Windows (Visual Studio), Linux (libx11-dev libv4l-dev), macOS (Xcode)
  • A 30-day free trial license for Dynamsoft Barcode Reader

Platform-Specific Requirements

Windows

  • Visual Studio 2019 or later (for building from source)
  • Media Foundation (included with Windows)
  • Windows 10/11 recommended

Linux

sudo apt update
sudo apt install libx11-dev libv4l-dev

macOS

  • Xcode development tools (for building from source)
  • AVFoundation framework (included with macOS)

Understand the Project Structure

This project consists of two main components:

  1. LiteCam SDK: A lightweight, cross-platform Java camera capture library
  2. Maven Barcode Scanner: A full-featured barcode scanning application with dual detection engines

    Java barcode scanner reader

Key Features

  • Real-time Camera Feed: High-performance camera capture using native JNI
  • Dual Barcode Engines: Switch between ZXing (open-source) and Dynamsoft (enterprise)
  • Visual Overlays: Real-time barcode highlighting with coordinates
  • File Mode Support: Drag-and-drop image processing
  • Cross-Platform: Windows, macOS, and Linux support
  • Convenience Scripts: Cross-platform build and run scripts for easy development

What LiteCam SDK Provides

LiteCam is a lightweight C++ camera SDK. A JNI bridge turns it into a Java-compatible library.

Core Features

  • Cross-Platform Video Capture: Uses platform-native APIs (Media Foundation, V4L2, AVFoundation)
  • RGB Frame Access: Direct access to uncompressed RGB data
  • JNI Integration: Optimized native bridge for Java applications
  • Resolution Control: Support for multiple resolutions and frame rates

Architecture

┌─────────────────┐    ┌──────────────┐    ┌─────────────────┐
│   Java App      │────│  LiteCam     │────│  Native Camera  │
│                 │    │  JNI Bridge  │    │  APIs           │
└─────────────────┘    └──────────────┘    └─────────────────┘

Supported Platforms

Platform Camera API Display
Windows Media Foundation GDI/DirectX
Linux Video4Linux (V4L2) X11
macOS AVFoundation Cocoa

Choose a Barcode Detection Engine

The Maven Barcode Scanner application demonstrates advanced integration of camera capture with multiple barcode detection engines.

Application Architecture

┌──────────────────┐
│   Swing GUI      │
├──────────────────┤
│  Camera Panel    │  ← Live preview with overlays
│  Controls Panel  │  ← Engine selection, modes
│  Results Panel   │  ← Detection history
└──────────────────┘
         │
├────────────────────┤
│    Core Engine     │
├────────────────────┤
│ ┌────────────────┐ │
│ │ LiteCam SDK    │ │  ← Camera capture
│ └────────────────┘ │
│ ┌────────────────┐ │
│ │ ZXing Engine   │ │  ← Open-source detection
│ └────────────────┘ │
│ ┌────────────────┐ │
│ │ Dynamsoft DBR  │ │  ← Enterprise detection
│ └────────────────┘ │
└────────────────────┘

Detection Engines Comparison

Feature ZXing Dynamsoft DBR
Cost Free (Apache 2.0) Commercial license
Accuracy Good Excellent
Speed Fast Very Fast
Damaged Codes Limited Advanced
Multi-detection Basic Advanced

Explore the Repository Layout

The project is organized into two main components:

├── README.md                    # Project overview and quick start
├── build-jar.ps1/.sh            # Scripts to build LiteCam SDK
├── run-litecam.ps1/.sh          # Scripts to test LiteCam SDK
├── litecam.jar                  # Pre-built Camera SDK with natives
├── include/                     # C++ headers for camera implementation
├── src/                         # C++ camera implementation (cross-platform)
├── java-src/                    # Basic LiteCam Java SDK source
│   └── com/example/litecam/
│       ├── LiteCam.java         # Main camera API
│       └── LiteCamViewer.java   # Simple camera viewer test
└── maven-example/               # Complete Barcode Scanner Application
    ├── pom.xml                  # Maven dependencies and build config
    ├── build.ps1/.sh            # Build scripts for barcode scanner
    ├── run.ps1/.sh              # Run scripts for barcode scanner
    ├── src/main/java/com/example/litecam/
    │   └── BarcodeScanner.java  # Main barcode scanning application
    └── target/                  # Maven build output
        └── litecam-barcode-scanner-1.0.0.jar

Build the Java Camera SDK with JNI

Step 1: Write the JNI Bridge for LiteCam

Create a LiteCamJNI.cpp file to wrap the LiteCam C++ SDK, enabling access from Java:

#include "Camera.h"
#include <jni.h>
#include <vector>
#include <mutex>
#include <string>

struct CameraEntry
{
    int id;
    Camera *cam;
};
static std::mutex g_mutex;
static std::vector<CameraEntry> g_cameras;
static int g_nextId = 1;

static Camera *getCamera(int handle)
{
    std::lock_guard<std::mutex> lock(g_mutex);
    for (auto &e : g_cameras)
        if (e.id == handle)
            return e.cam;
    return nullptr;
}

static int registerCamera(Camera *c)
{
    std::lock_guard<std::mutex> lock(g_mutex);
    int id = g_nextId++;
    g_cameras.push_back({id, c});
    return id;
}

static void unregisterCamera(int handle)
{
    std::lock_guard<std::mutex> lock(g_mutex);
    for (auto it = g_cameras.begin(); it != g_cameras.end(); ++it)
    {
        if (it->id == handle)
        {
            delete it->cam;
            g_cameras.erase(it);
            return;
        }
    }
}

static jclass findAndGlobalRef(JNIEnv *env, const char *name)
{
    jclass local = env->FindClass(name);
    return (jclass)env->NewGlobalRef(local);
}

extern "C"
{

    JNIEXPORT jobjectArray JNICALL Java_com_example_litecam_LiteCam_listDevices(JNIEnv *env, jclass)
    {
        auto devices = ListCaptureDevices();
        jclass stringClass = env->FindClass("java/lang/String");
        jobjectArray arr = env->NewObjectArray((jsize)devices.size(), stringClass, nullptr);
        for (jsize i = 0; i < (jsize)devices.size(); ++i)
        {
#ifdef _WIN32
            char buffer[512];
            wcstombs_s(nullptr, buffer, devices[i].friendlyName, sizeof(buffer));
            env->SetObjectArrayElement(arr, i, env->NewStringUTF(buffer));
#else
            env->SetObjectArrayElement(arr, i, env->NewStringUTF(devices[i].friendlyName));
#endif
        }
        return arr;
    }

    JNIEXPORT jint JNICALL Java_com_example_litecam_LiteCam_open(JNIEnv *env, jobject self, jint deviceIndex)
    {
        auto cam = new Camera();
        if (!cam->Open(deviceIndex))
        {
            delete cam;
            return 0;
        }
        return registerCamera(cam);
    }

    JNIEXPORT void JNICALL Java_com_example_litecam_LiteCam_nativeClose(JNIEnv *, jobject, jint handle)
    {
        unregisterCamera(handle);
    }

    JNIEXPORT jintArray JNICALL Java_com_example_litecam_LiteCam_listSupportedResolutions(JNIEnv *env, jobject, jint handle)
    {
        Camera *cam = getCamera(handle);
        if (!cam)
            return nullptr;
        auto mts = cam->ListSupportedMediaTypes();
        // Flatten as width,height pairs sequentially.
        jintArray arr = env->NewIntArray((jsize)(mts.size() * 2));
        std::vector<jint> tmp;
        tmp.reserve(mts.size() * 2);
        for (auto &m : mts)
        {
            tmp.push_back((jint)m.width);
            tmp.push_back((jint)m.height);
        }
        env->SetIntArrayRegion(arr, 0, (jsize)tmp.size(), tmp.data());
        return arr;
    }

    JNIEXPORT jboolean JNICALL Java_com_example_litecam_LiteCam_setResolution(JNIEnv *, jobject, jint handle, jint w, jint h)
    {
        Camera *cam = getCamera(handle);
        if (!cam)
            return JNI_FALSE;
        return cam->SetResolution(w, h) ? JNI_TRUE : JNI_FALSE;
    }

    JNIEXPORT jboolean JNICALL Java_com_example_litecam_LiteCam_captureFrame(JNIEnv *env, jobject, jint handle, jobject byteBuffer)
    {
        Camera *cam = getCamera(handle);
        if (!cam)
            return JNI_FALSE;
        FrameData frame = cam->CaptureFrame();
        if (!frame.rgbData)
            return JNI_FALSE;
        unsigned char *dst = (unsigned char *)env->GetDirectBufferAddress(byteBuffer);
        if (!dst)
        {
            ReleaseFrame(frame);
            return JNI_FALSE;
        }
        size_t expected = (size_t)(frame.width * frame.height * 3);
        memcpy(dst, frame.rgbData, expected < frame.size ? expected : frame.size);
        ReleaseFrame(frame);
        return JNI_TRUE;
    }

    JNIEXPORT jint JNICALL Java_com_example_litecam_LiteCam_getFrameWidth(JNIEnv *, jobject, jint handle)
    {
        Camera *cam = getCamera(handle);
        if (!cam)
            return 0;
        return (jint)cam->frameWidth;
    }
    JNIEXPORT jint JNICALL Java_com_example_litecam_LiteCam_getFrameHeight(JNIEnv *, jobject, jint handle)
    {
        Camera *cam = getCamera(handle);
        if (!cam)
            return 0;
        return (jint)cam->frameHeight;
    }

} // extern C

Step 2: Configure CMake to Build the JNI Shared Library

The following CMakeLists.txt file is used to build the JNI shared library:


cmake_minimum_required(VERSION 3.15)

# Project name and version
project(CameraProject VERSION 1.0 LANGUAGES CXX)

# Set C++ standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# Build type
if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE Release)
endif()


# Define include directories
set(INCLUDE_DIR ${CMAKE_SOURCE_DIR}/include)

# Platform detection
if(WIN32)
    set(PLATFORM_NAME "windows")
elseif(APPLE)
    set(PLATFORM_NAME "macos")
elseif(UNIX)
    set(PLATFORM_NAME "linux")
else()
    set(PLATFORM_NAME "unknown")
endif()

# Architecture detection
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
    set(ARCH_NAME "x86_64")
else()
    set(ARCH_NAME "x86")
endif()

# Compiler-specific settings
if(MSVC)
    # Set runtime library for Windows
    if(CMAKE_BUILD_TYPE STREQUAL "Debug")
        set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDebug")
    else()
        set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded") 
    endif()
    
    # Enable parallel compilation
    add_compile_options(/MP)
    
    # Disable specific warnings
    add_compile_options(/wd4251 /wd4275)
    
    # Enable UTF-8 encoding
    add_compile_options(/utf-8)
endif()

# Define source files for the Camera library based on platform
if (WIN32)
    set(LIBRARY_SOURCES
        src/CameraWindows.cpp
        src/CameraPreviewWindows.cpp
    )
elseif (UNIX AND NOT APPLE)
    set(LIBRARY_SOURCES
        src/CameraLinux.cpp
        src/CameraPreviewLinux.cpp
    )
elseif (APPLE)
    # Support universal binaries on macOS
    set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64")
    
    # Ensure that Objective-C++ source files are compiled as Objective-C++
    set(LIBRARY_SOURCES
        src/CameraMacOS.mm
        src/CameraPreviewMacOS.mm
    )
    set_source_files_properties(src/CameraMacOS.mm src/CameraPreviewMacOS.mm PROPERTIES COMPILE_FLAGS "-x objective-c++")

    # Set main.cpp to be treated as Objective-C++ for macOS
    set_source_files_properties(src/main.cpp PROPERTIES COMPILE_FLAGS "-x objective-c++")
    
endif()

# Add JNI wrapper source (common for all platforms)
list(APPEND LIBRARY_SOURCES
    src/LiteCamJNI.cpp
)

# Define source files for the executable
set(EXECUTABLE_SOURCES
    src/main.cpp
)

# Add the Camera shared library
add_library(litecam SHARED ${LIBRARY_SOURCES})

# Set library properties
set_target_properties(litecam PROPERTIES
    VERSION ${PROJECT_VERSION}
    SOVERSION ${PROJECT_VERSION_MAJOR}
    OUTPUT_NAME "litecam"
)

# Platform-specific library naming
if(WIN32)
    set_target_properties(litecam PROPERTIES
        PREFIX ""
        SUFFIX ".dll"
    )
elseif(APPLE)
    set_target_properties(litecam PROPERTIES
        PREFIX "lib"
        SUFFIX ".dylib"
    )
else()
    set_target_properties(litecam PROPERTIES
        PREFIX "lib"
        SUFFIX ".so"
    )
endif()

# Set include directories for the Camera library
target_include_directories(litecam PUBLIC 
    $<BUILD_INTERFACE:${INCLUDE_DIR}>
    $<INSTALL_INTERFACE:include>
)

# Define the CAMERA_EXPORTS macro for the shared library
target_compile_definitions(litecam PRIVATE 
    CAMERA_EXPORTS
    LITECAM_VERSION_MAJOR=${PROJECT_VERSION_MAJOR}
    LITECAM_VERSION_MINOR=${PROJECT_VERSION_MINOR}
    LITECAM_VERSION_PATCH=${PROJECT_VERSION_PATCH}
)

# Platform-specific dependencies for the Camera library
if (UNIX AND NOT APPLE)
    # Linux dependencies
    find_package(X11 REQUIRED)
    find_package(PkgConfig REQUIRED)
    
    # Check for Video4Linux2
    pkg_check_modules(V4L2 libv4l2)
    
    if (X11_FOUND)
        target_include_directories(litecam PUBLIC ${X11_INCLUDE_DIR})
        target_link_libraries(litecam PRIVATE ${X11_LIBRARIES} pthread)
    endif()
    
    if (V4L2_FOUND)
        target_include_directories(litecam PRIVATE ${V4L2_INCLUDE_DIRS})
        target_link_libraries(litecam PRIVATE ${V4L2_LIBRARIES})
    else()
        message(WARNING "Video4Linux2 not found - camera functionality may be limited")
    endif()
    
elseif (APPLE)
    # macOS dependencies
    find_library(COCOA_LIBRARY Cocoa REQUIRED)
    find_library(AVFOUNDATION_LIBRARY AVFoundation REQUIRED)
    find_library(COREMEDIA_LIBRARY CoreMedia REQUIRED)
    find_library(COREVIDEO_LIBRARY CoreVideo REQUIRED)
    find_library(OBJC_LIBRARY objc REQUIRED)

    target_link_libraries(litecam PRIVATE 
        ${COCOA_LIBRARY} 
        ${AVFOUNDATION_LIBRARY} 
        ${COREMEDIA_LIBRARY} 
        ${COREVIDEO_LIBRARY} 
        ${OBJC_LIBRARY}
    )
    
elseif (WIN32)
    # Windows dependencies
    target_link_libraries(litecam PRIVATE 
        ole32 
        uuid 
        mfplat 
        mf 
        mfreadwrite 
        mfuuid
    )
    
endif()

# JNI support - enhanced detection
find_package(JNI)
if (JNI_FOUND)
    target_include_directories(litecam PRIVATE ${JNI_INCLUDE_DIRS})
    target_compile_definitions(litecam PRIVATE LITECAM_JNI_ENABLED)
    
    # Add JNI libraries on some platforms
    if(WIN32)
        # Windows doesn't typically need to link JNI libraries
    elseif(APPLE)
        # macOS typically has JNI in the framework
    else()
        # Linux might need explicit JNI library linking
        if(JNI_LIBRARIES)
            target_link_libraries(litecam PRIVATE ${JNI_LIBRARIES})
        endif()
    endif()
endif()

# Optional: Add position independent code for shared library
set_property(TARGET litecam PROPERTY POSITION_INDEPENDENT_CODE ON)

# Add the camera_capture executable
add_executable(camera_capture ${EXECUTABLE_SOURCES})

# Set executable properties
set_target_properties(camera_capture PROPERTIES
    OUTPUT_NAME "camera_capture"
)

# Link the Camera library to the executable
target_link_libraries(camera_capture PRIVATE litecam)

# Include the shared library's headers in the executable
target_include_directories(camera_capture PRIVATE ${INCLUDE_DIR})

# For macOS, link against the frameworks for the executable too
if (APPLE)
    target_link_libraries(camera_capture PRIVATE 
        ${COCOA_LIBRARY} 
        ${AVFOUNDATION_LIBRARY} 
        ${COREMEDIA_LIBRARY} 
        ${COREVIDEO_LIBRARY} 
        ${OBJC_LIBRARY}
    )
endif()

# Installation rules (optional)
install(TARGETS litecam camera_capture
    EXPORT CameraProjectTargets
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib
    RUNTIME DESTINATION bin
    INCLUDES DESTINATION include
)

install(DIRECTORY ${INCLUDE_DIR}/
    DESTINATION include
    FILES_MATCHING PATTERN "*.h"
)

# Export targets for find_package support
install(EXPORT CameraProjectTargets
    FILE CameraProjectTargets.cmake
    NAMESPACE CameraProject::
    DESTINATION lib/cmake/CameraProject
)

# Generate and install package config files
include(CMakePackageConfigHelpers)

configure_package_config_file(
    "${CMAKE_CURRENT_SOURCE_DIR}/cmake/CameraProjectConfig.cmake.in"
    "${CMAKE_CURRENT_BINARY_DIR}/CameraProjectConfig.cmake"
    INSTALL_DESTINATION lib/cmake/CameraProject
)

write_basic_package_version_file(
    "${CMAKE_CURRENT_BINARY_DIR}/CameraProjectConfigVersion.cmake"
    VERSION ${PROJECT_VERSION}
    COMPATIBILITY SameMajorVersion
)

Step 3: Expose the Camera API to Java via JNI

package com.example.litecam;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

public class LiteCam implements AutoCloseable {
    static {
        boolean loaded = false;
        try {
            loaded = loadBundled();
        } catch (Throwable t) {
        }
        if (!loaded) {
            System.loadLibrary("litecam");
        }
    }

    private static boolean loadBundled() throws Exception {
        String os = System.getProperty("os.name").toLowerCase();
        String arch = System.getProperty("os.arch").toLowerCase();
        String osToken;
        if (os.contains("win")) osToken = "windows"; else if (os.contains("mac") || os.contains("darwin")) osToken = "macos"; else if (os.contains("nux") || os.contains("linux")) osToken = "linux"; else return false;
        String archToken;
        if (arch.contains("aarch64") || arch.contains("arm64")) archToken = "arm64"; else if (arch.contains("64")) archToken = "x86_64"; else archToken = arch; // fallback

        String libBase = "litecam";
        String ext = osToken.equals("windows") ? ".dll" : (osToken.equals("macos") ? ".dylib" : ".so");
        String resourcePath = "/natives/" + osToken + "-" + archToken + "/" + (osToken.equals("windows") ? libBase + ext : "lib" + libBase + ext);
        try (java.io.InputStream in = LiteCam.class.getResourceAsStream(resourcePath)) {
            if (in == null) return false;
            java.nio.file.Path tempFile = java.nio.file.Files.createTempFile(libBase + "-", ext);
            try (java.io.OutputStream out = java.nio.file.Files.newOutputStream(tempFile)) {
                byte[] buf = new byte[8192]; int r; while ((r = in.read(buf)) != -1) out.write(buf, 0, r);
            }
            tempFile.toFile().deleteOnExit();
            System.load(tempFile.toAbsolutePath().toString());
            return true;
        }
    }

    private int handle = 0;

    // Native methods
    public static native String[] listDevices();
    private native int open(int deviceIndex);
    private native void nativeClose(int handle);
    public native int[] listSupportedResolutions(int handle); 
    public native boolean setResolution(int handle, int width, int height);
    public native boolean captureFrame(int handle, ByteBuffer rgbOut); 
    public native int getFrameWidth(int handle);
    public native int getFrameHeight(int handle);

    public void openDevice(int index) {
        if (handle != 0) throw new IllegalStateException("Already opened");
        handle = open(index);
        if (handle == 0) throw new RuntimeException("Failed to open camera index " + index);
    }

    public void closeDevice() {
        if (handle != 0) {
        nativeClose(handle);
            handle = 0;
        }
    }

    @Override
    public void close() { closeDevice(); }

    public List<int[]> getSupportedResolutions() {
        int[] flat = listSupportedResolutions(handle);
        List<int[]> list = new ArrayList<>();
        if (flat != null) {
            for (int i=0;i+1<flat.length;i+=2) {
                list.add(new int[]{flat[i], flat[i+1]});
            }
        }
        return list;
    }

    public boolean setResolution(int w, int h) { return setResolution(handle, w, h); }

    public int getWidth() { return getFrameWidth(handle); }
    public int getHeight() { return getFrameHeight(handle); }

    public boolean grabFrame(ByteBuffer dst) { return captureFrame(handle, dst); }

    public boolean isOpen() { return handle != 0; }
}

Step 4: Package the Native Library and Java Classes into a JAR

  1. Build native library with CMake:
     mkdir build
     cd build
     cmake .. -DCMAKE_BUILD_TYPE=Release
     cmake --build . --config Release
    
  2. Compile Java sources:

     cd ..
     javac -d build -h include java-src/com/example/litecam/*.java
    
  3. Create JAR with native library:

     jar cf litecam.jar -C build com
     jar uf litecam.jar build/litecam.dll # or .dylib on macOS, .so on Linux
    

Integrate Barcode Scanning into the Java Application

The following code snippet demonstrates the basic usage of LiteCam, ZXing, and Dynamsoft Barcode Reader APIs.

Capture Frames with LiteCam

LiteCam cam = new LiteCam();

String[] devices = LiteCam.listDevices();
for (int i = 0; i < devices.length; i++) {
    System.out.println(i + ": " + devices[i]);
}

cam.openDevice(0);

cam.setResolution(640, 480);

ByteBuffer buffer = ByteBuffer.allocateDirect(640 * 480 * 3);
if (cam.grabFrame(buffer)) {
    byte[] frameData = new byte[buffer.remaining()];
    buffer.get(frameData);
}

cam.close();

Decode Barcodes with ZXing

import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.multi.GenericMultipleBarcodeReader;

public class ZXingDetector {
    private MultiFormatReader reader;
    private GenericMultipleBarcodeReader multiReader;
    
    public void initialize() {
        reader = new MultiFormatReader();
        multiReader = new GenericMultipleBarcodeReader(reader);
    }
    
    public List<Result> detectBarcodes(BufferedImage image) {
        List<Result> results = new ArrayList<>();
        
        try {
            LuminanceSource source = new BufferedImageLuminanceSource(image);
            BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
            
            try {
                Result[] multiResults = multiReader.decodeMultiple(bitmap);
                results.addAll(Arrays.asList(multiResults));
            } catch (NotFoundException e) {
                try {
                    Result singleResult = reader.decode(bitmap);
                    results.add(singleResult);
                } catch (NotFoundException ignored) {
                }
            }
        } catch (Exception e) {
            logger.debug("ZXing detection failed: {}", e.getMessage());
        }
        
        return results;
    }
}

Decode Barcodes with Dynamsoft Barcode Reader

Start your 30-day free trial to get a license key before running this code.

import com.dynamsoft.dbr.*;
import com.dynamsoft.core.basic_structures.ImageData;

public class DynamsoftDetector {
    private CaptureVisionRouter cvRouter;
    
    public void initialize() throws Exception {
        LicenseManager.initLicense("LICENSE-KEY");
        
        cvRouter = new CaptureVisionRouter();
    }
    
    public List<BarcodeResultItem> detectBarcodes(BufferedImage image) {
        List<BarcodeResultItem> results = new ArrayList<>();
        
        try {
            ImageData imageData = createImageData(image);
            
            CapturedResult result = cvRouter.capture(imageData, 
                EnumPresetTemplate.PT_READ_BARCODES);
            
            DecodedBarcodesResult barcodeResult = result.getDecodedBarcodesResult();
            if (barcodeResult != null) {
                BarcodeResultItem[] items = barcodeResult.getItems();
                if (items != null) {
                    results.addAll(Arrays.asList(items));
                }
            }
        } catch (Exception e) {
            logger.error("Dynamsoft detection failed: {}", e.getMessage());
        }
        
        return results;
    }
    
    private ImageData createImageData(BufferedImage image) {
    }
}

Common Issues & Edge Cases

  • Native library not found at runtime: If System.loadLibrary("litecam") fails, confirm the .dll/.so/.dylib is either on java.library.path or bundled under natives/<os>-<arch>/ inside the JAR. On Linux, also verify libv4l-dev and libx11-dev are installed before building.
  • Camera opens but grabFrame always returns false: This typically means the frame buffer size passed to ByteBuffer.allocateDirect does not match the actual resolution reported by getWidth()/getHeight() after setResolution(). Always query width and height after setting resolution before allocating the buffer.
  • ZXing misses barcodes that Dynamsoft decodes: ZXing uses a single-pass binarization strategy (HybridBinarizer) and struggles with low-contrast, tilted, or partially obscured codes. Switching to Dynamsoft Barcode Reader’s PT_READ_BARCODES preset resolves most of these cases without additional configuration.
  • initLicense blocks or times out: Dynamsoft license validation requires an outbound HTTPS connection on first use. In air-gapped environments, request an offline license from Dynamsoft support.

Source Code

https://github.com/yushulx/java-jni-barcode-qrcode-reader/tree/main/examples/barcode-scanner