Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing a USB camera using Android-Camera2 API

I have a UVC camera that want to access and grab frames from in my Android Pie (Android 9) code.

This is the code I'm using to enumerate the cameras connected to the Android phone:

    @Override
    public void onResume()
    {
        CameraManager manager =
                (CameraManager)getSystemService(CAMERA_SERVICE);
        try {
            for (String cameraId : manager.getCameraIdList()) {
                CameraCharacteristics chars
                        = manager.getCameraCharacteristics(cameraId);
                // Do something with the characteristics
                int deviceLevel = chars.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
                Log.d(TAG, " **** device ["+cameraId+"] level:"+deviceLevel);
            }
        } catch(CameraAccessException e){
            e.printStackTrace();
        }
    }

I was hopping on Android 9/Pie (Pixel 3) this shows the connected USB camera. But only two devices get listed, the front and back cameras og the phone.

This is the list of features and permissions in my manifest file (AndroidManifest.xml):

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-feature android:name="android.hardware.usb.host" />

I cannot find any sample code on the internet how to achieve this, i.e. access the USB camera using on Android 9 and later.

What am I missing to get my code enumerate the USB camera? I have seen some 3rd party UVC libraries for Android, but I don't to use them and want to use native Android code.

like image 340
alex papa Avatar asked Sep 08 '19 23:09

alex papa


3 Answers

I spent way too long trying to get this to work, so I thought I'd share my findings with whoever reads this. Much of it has already been mentioned by previous posters, but hopefully I can add something of value.

  1. Android has the Camera2 API which seems to support external cameras. You can supposedly get the characteristics for each attached camera and look for one that is LENS_FACING_EXTERNAL.

  2. Practically speaking this doesn't seem to work on many phones yet. I tried it on three Samsung phones (S9, A31, A51) and one Google Pixel 2 XL. This gave me a mix of Android 10 and 11. None of the phones supported external cameras.

  3. There are applications, like USB Camera, that still manages to make it work. I believe they are making use of libraries like UVCCamera under the hood. This is basically a complete UVC (USB Video Class) implementation + USB Host, which is what eventually allows you to capture the video.

  4. The reason I decided against using UVCCamera myself is that it doesn't have the "Android Blessing", and things might break in weird ways on different devices, or during Android upgrades. I myself don't have the capacity to support that.

  5. You can easily download and build the Camera2Video sample (./gradlew assembleRelease -x test) since that gives you a ready made app which shows you all the detected cameras. You would need a USB OTG adapter to plug your webcam into your phone, but you can get those for a few bucks.

As things stand right now support for external USB cameras on Android is a bit of a sad story. There doesn't seem to be an officially supported way to make use of external USB cameras which works on most devices.

Here's a bit of sample code that allows you to try to enumerate the cameras on your own phone if you feel like it. It's just a few snippets lifted from my own experimental code, so error handling / correctness is bad. However, it's still the most important bits and pieces of code you need to know about AFAIK.

public class CameraUtils {
    final Context context;
    final CameraManager cameraManager;
    
    public CameraUtils(Context context) {      
        this.context = context;  
        this.cameraManager = (CameraManager)context.getSystemService(Context.CAMERA_SERVICE);
    }
    
    /** 
    * Get the IDs of all available cameras.
    */
    public String[] getCameraIds() {
        return this.cameraManager.getCameraIdList();
    }
        
    /**
    * Get the "lens facing" for a particular camera ID returned by `getCameraIds()`.
    */
    public bool getLensFacing(String cameraId) {        
        CameraCharacteristics characteristics = this.cameraManager.getCameraCharacteristics(cameraId);
        
        // This will return one of CameraMetadata.LENS_FACING_FRONT,
        // CameraMetadata.LENS_FACING_BACK or CameraMetadata.LENS_FACING_EXTERNAL.
        return characteristics.get(CameraCharacteristics.LENS_FACING);
    }
    
    /**
    * Return true if this kernel supports external cameras, false otherwise.
    */
    public bool supportsExternalCameras() {
        return this.context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_EXTERNAL);        
    }
}

P.S. I've tried my best to make sure this information is correct. If I got something wrong, please don't hesitate to correct me! The documentation on how to use external USB cameras on Android is sparse at best.

like image 178
Erik Näslund Avatar answered Oct 17 '22 08:10

Erik Näslund


@alex papa, I bought a Samsung Galaxy S10e, tried with logitech c920, but didn't get external camera ID. Can you please share what webcam you use? I also packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_EXTERNAL) is returning false.

like image 38
Yu He Avatar answered Oct 17 '22 08:10

Yu He


You can call PackageManager.hasSystemFeature with the constant FEATURE_CAMERA_EXTERNAL to check if your device supports external cameras. Pixel 3 does not support this feature.

Indeed, there are multiple libraries and applications, which offers USB camera support. But they do not use android API and implement everything themselves. The most common approach seems to use Video for linux kernel module (if the kernel was compiled with this module). It also requires permissions to access /dev/video devices, which is not usually the case on non-rooted devices. But the implementation is fairly simple. E.g. android-webcam uses this approach.

Another approach is to use USB host api and implement all the protocols yourself. This does not require root access, but implementation is far more complex. You can check this library which goes this way.

like image 32
esentsov Avatar answered Oct 17 '22 09:10

esentsov