How to Build an Android Barcode Scanner with an IP Camera via RTSP

An IP camera, also known as a network camera, is a digital video tool that sends and receives data via the internet. Unlike its analogue predecessor, the CCTV (Closed Circuit TV), it offers the functionality to view live or recorded footage from anywhere, anytime using a web-based platform or mobile application.

IP cameras are widely used in surveillance. They can also be used for inventory management or serve as an extra camera for devices like Android tablets and kiosks.

In this article, we are going to create an Android barcode scanner using an IP camera. ExoPlayer is used to play the camera stream of IP cameras via RTSP and Dynamsoft Barcode Reader is used for barcode scanning.

What you’ll build: An Android app that streams an IP camera feed over RTSP using ExoPlayer and decodes barcodes from the video frames in real time with Dynamsoft Barcode Reader.

Key Takeaways

  • Android apps can scan barcodes from IP camera RTSP streams by capturing video frames with PixelCopy and decoding them with Dynamsoft Barcode Reader.
  • ExoPlayer (AndroidX Media3) natively supports RTSP, making it the simplest way to display an IP camera feed in an Android app.
  • A timer-based frame capture loop at 100 ms intervals provides near-real-time barcode detection without blocking the UI thread.
  • This approach works for warehouse inventory scanners, kiosk barcode readers, and any scenario where a device-mounted camera is impractical.

Common Developer Questions

  • How do I scan barcodes from an IP camera on Android?
  • How do I play an RTSP stream in an Android app using ExoPlayer?
  • How do I capture frames from a SurfaceView for barcode decoding in Android?

Keywords:

  • RTSP (Real-Time Streaming Protocol) is a protocol for streaming media through the network.
  • ExoPlayer is an extensible media player for Android which supports RTSP.

Prerequisites

Step 1: Create a New Android Project

Use Android Studio to create an app project with an empty activity.

Step 2: Add ExoPlayer and Barcode Reader Dependencies

  1. Add ExoPlayer in your app’s build.gradle.

    implementation "androidx.media3:media3-exoplayer:1.4.1"
    implementation "androidx.media3:media3-exoplayer-dash:1.4.1"
    implementation "androidx.media3:media3-ui:1.4.1"
    implementation "androidx.media3:media3-exoplayer-rtsp:1.4.1"
    
  2. Add Dynamsoft Barcode Reader.

    1. Open settings.gradle to add Dynamsoft’s maven repo.

      dependencyResolutionManagement {
          repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
          repositories {
              google()
              mavenCentral()
              maven {
                  url "https://download2.dynamsoft.com/maven/aar"
              }
          }
      }
      
    2. Declare the dependency in the app’s build.gradle.

      implementation 'com.dynamsoft:dynamsoftbarcodereaderbundle:10.4.2000'
      

Step 3: Add Internet Permission

Open AndroidManifest.xml to add the Internet permission.

<uses-permission android:name="android.permission.INTERNET"></uses-permission>

Step 4: Stream the IP Camera Feed with ExoPlayer via RTSP

  1. Open activity_main.xml to add the PlayerView.

    <androidx.media3.ui.PlayerView
        android:id="@+id/playerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:surface_type="surface_view" >
    </androidx.media3.ui.PlayerView>
    

    Get the view in MainActivity.java.

    private PlayerView playerView;
    playerView = findViewById(R.id.playerView);
    
  2. Create an instance of ExoPlayer and set it as PlayerView’s player.

    ExoPlayer player = new ExoPlayer.Builder(getApplicationContext()).build();
    playerView.setPlayer(player);
    
  3. Set the RTSP stream as the media source for the player. You can start a local RTSP server using mediamtx for testing.

    protected Uri uri = Uri.parse("rtsp://192.168.8.65:8554/mystream");
    MediaSource mediaSource =
                 new RtspMediaSource.Factory()
                    .createMediaSource(MediaItem.fromUri(uri));
    player.setMediaSource(mediaSource);
    
  4. Prepare the player and play the stream when it is ready.

    player.setPlayWhenReady(true);
    player.prepare();
    

Step 5: Decode Barcodes from the IP Camera Stream

  1. Initialize the license for Dynamsoft Barcode Reader. You can apply for a license here.

    LicenseManager.initLicense("LICENSE-KEY", this, (isSuccess, error) -> {
        if (!isSuccess) {
            error.printStackTrace();
        }
    });
    
  2. Create an instance of capture vision router to perform the barcode reading.

    private CaptureVisionRouter mRouter;
    mRouter = new CaptureVisionRouter(this);
    
  3. Start a timer to capture frames of the IP camera stream and read barcodes from them. Display the result in a TextView.

    private boolean decoding = false;
    private boolean isVideoPlaying = false;
    private Timer timer = null;
    private void startDecoding(){
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                takePhotoAndDecode();
            }
        };
        timer = new Timer();
        timer.schedule(task, 1000,100);
    }
       
     @OptIn(markerClass = UnstableApi.class)
     @SuppressLint("NewApi")
     private void takePhotoAndDecode() {
        if (decoding == false && isVideoPlaying) {
            decoding = true;
            SurfaceView surfaceView = (SurfaceView) playerView.getVideoSurfaceView();
            // Create a bitmap the size of the scene view.
            final Bitmap bitmap = Bitmap.createBitmap(surfaceView.getWidth(), surfaceView.getHeight(),
                    Bitmap.Config.ARGB_8888);
            // Create a handler thread to offload the processing of the image.
            final HandlerThread handlerThread = new HandlerThread("PixelCopier");
            handlerThread.start();
            // Make the request to copy.
            PixelCopy.request(surfaceView, bitmap, (copyResult) -> {
                if (copyResult == PixelCopy.SUCCESS) {
                    CapturedResult result =  mRouter.capture(bitmap, EnumPresetTemplate.PT_READ_BARCODES);
                    DecodedBarcodesResult decodedBarcodesResult = result.getDecodedBarcodesResult();
                    if (decodedBarcodesResult != null) {
                        BarcodeResultItem[] items = decodedBarcodesResult.getItems();
                        StringBuilder sb = new StringBuilder();
                        for (BarcodeResultItem item:items) {
                            sb.append(item.getFormatString());
                            sb.append(": ");
                            sb.append(item.getText());
                            sb.append("\n");
                        }
                        runOnUiThread(new Runnable() {
                            public void run() {
                                textView.setText(sb.toString());
                            }
                        });
                    }
                }
                decoding = false;
                handlerThread.quitSafely();
            }, new Handler(handlerThread.getLooper()));
        }
    }
    
  4. We can check whether the video is playing by adding a listener for the player.

    player.addListener(
        new Player.Listener() {
            @Override
            public void onIsPlayingChanged(boolean isPlaying) {
                isVideoPlaying = isPlaying;
            }
        });
    

Common Issues & Edge Cases

  • RTSP connection fails or stream does not load: Verify the IP camera’s RTSP URL, port, and credentials. Ensure the Android device and the camera are on the same network. Firewalls or NAT rules can block RTSP traffic on port 554 (or the custom port).
  • PixelCopy returns a blank or black bitmap: This can happen if the SurfaceView has not rendered its first frame yet. The timer-based approach guards against this with the isVideoPlaying flag, but on slow networks the first valid frame may take several seconds.
  • Barcode detection is slow or misses codes: The 100 ms timer interval may be too aggressive for low-powered devices. Increase the interval or switch to a lower-resolution capture to reduce CPU load. Also ensure adequate lighting on the barcodes filmed by the IP camera.

Source Code

Check out the source code of the demo to have a try:

https://github.com/tony-xlh/IPCam-Barcode-Scanner-Android