Android Question Does OTG camera work with B4A?

Alessandra Pellegri

Active Member
Licensed User
Longtime User
Does OTG camera work with B4A? How could I obtain the relative ID?

Obviously considering that my phone already has the correct driver for the camera and it works fine with various app.

Thank you
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
You should test it with Camera2.

Add this code at the end of CamEx2.Initialize:
B4X:
Dim ids() As String = jcamera.GetFieldJO("cameraManager").RunMethod("getCameraIdList", Null)
For Each id1 As String In ids
   Log(id1)
Next
It should print a number for each camera. On most devices it will print two numbers.
 
Upvote 0

Alessandra Pellegri

Active Member
Licensed User
Longtime User
At the moment I am using B4A just for hobby, so my version remained 6.80. So I cannot test with CamEx2.

Anyway I did this test with CAMERA1 and CAMERA2 api and the result was bad.

Here my code:
B4X:
#Region  Project Attributes
   #ApplicationLabel: B4A Example
   #VersionCode: 1
   #VersionName:
   'SupportedOrientations possible values: unspecified, landscape or portrait.
   #SupportedOrientations: unspecified
   #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
   #FullScreen: False
   #IncludeTitle: True
   #BridgeLogger: True
#End Region

Sub Process_Globals
   'These global variables will be declared once when the application starts.
   'These variables can be accessed from all modules.

End Sub

Sub Globals
   'These global variables will be redeclared each time the activity is created.
   'These variables can only be accessed from this module.
   Private NativeMe As JavaObject
End Sub

Sub Activity_Create(FirstTime As Boolean)
   'Do not forget to load the layout file created with the visual designer. For example:
   Activity.LoadLayout("Layout1")
   If FirstTime Then
       NativeMe.InitializeContext
   End If
End Sub

Sub Activity_Resume
   Dim c1 As Int = NativeMe.RunMethod("camera1CameraCount", Null)
   Log("With Camera1: "&c1)
   Dim c2 As Int = NativeMe.RunMethod("camera2CameraCount", Null)
   Log("With Camera2: "&c2) 
End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub


#If Java

import android.hardware.Camera;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraAccessException;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.CameraInfo;
import android.content.pm.PackageManager;
import android.content.Context;


public int camera1CameraCount() {
   try {
        int numberOfCameras = Camera.getNumberOfCameras();
       return numberOfCameras;
   } catch (Exception e) {
       return -1;
   }
}

public int camera2CameraCount() {
CameraManager manager;
manager=(CameraManager)getSystemService(Context.CAMERA_SERVICE);
  try {
       return manager.getCameraIdList().length;
  } catch (CameraAccessException e) {
    return -1;
  }
}

#End If

The result is :

B4X:
With Camera1: 2
With Camera2: 2

But OTG View app works, so how it does ?
 
Upvote 0

Alessandra Pellegri

Active Member
Licensed User
Longtime User
Here, it explain that I need an unique serial number to identify the external camera : https://source.android.com/reference/hidl/android/hardware/camera/provider/2.4/ICameraProvider

But it isn't USB ID as I thought because I tried but it doesn't work.

What you think that this sentence means ?
getCameraDeviceList:

Returns the list of internal camera device interfaces known to this camera provider.These devices can then be accessed via the hardware service manager.

External camera devices(camera facing EXTERNAL)must be reported through the device status change callback, not in this list.Only devices with facing BACK or FRONT must be listed here.

Here my test code:
B4X:
#Region  Project Attributes
   #ApplicationLabel: B4A Example
   #VersionCode: 1
   #VersionName:
   'SupportedOrientations possible values: unspecified, landscape or portrait.
   #SupportedOrientations: unspecified
   #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
   #FullScreen: False
   #IncludeTitle: True
   #BridgeLogger: True
#End Region

Sub Process_Globals
   'These global variables will be declared once when the application starts.
   'These variables can be accessed from all modules.

End Sub

Sub Globals
   'These global variables will be redeclared each time the activity is created.
   'These variables can only be accessed from this module.
   Private NativeMe As JavaObject
End Sub

Sub Activity_Create(FirstTime As Boolean)
   'Do not forget to load the layout file created with the visual designer. For example:
   Activity.LoadLayout("Layout1")
   If FirstTime Then
       NativeMe.InitializeContext
       USBdetails.Initialize
   End If
End Sub

Sub Activity_Resume
   Dim c1 As Int = NativeMe.RunMethod("camera1CameraCount", Null)
   Log("With Camera1: "&c1)
   Dim c2 As Int = NativeMe.RunMethod("camera2CameraCount", Null)
   Log("With Camera2: "&c2)
   USBdetails.GetDevices
   Log("USB devices: "&USBdetails.NumberOfDevices)
   Dim a As Int
   For a = 0 To USBdetails.NumberOfDevices-1
       Log("USB "&a&": DeviceClass = "&USBdetails.devices(a).DeviceClass)
       Log("USB "&a&": DeviceId = "&USBdetails.devices(a).DeviceId)
       Log("USB "&a&": DeviceName = "&USBdetails.devices(a).DeviceName)
       Log("USB "&a&": DeviceSubclass = "&USBdetails.devices(a).DeviceSubclass)
       Log("USB "&a&": InterfaceCount = "&USBdetails.devices(a).InterfaceCount)
       Log("USB "&a&": ProductId = "&USBdetails.devices(a).ProductId)
       Log("USB "&a&": VendorId = "&USBdetails.devices(a).VendorId)
       Dim tmpS As String = USBdetails.devices(a).DeviceId
       Dim c3 As String = NativeMe.RunMethod("openUSBCamera", Array(tmpS))
       Log("Opening Camera USB "&a&" result :"&c3)
   Next
   Dim c3 As String  = NativeMe.RunMethod("openUSBCamera", Array("0"))
   Log("Opening Camera 0 result :"&c3)
   Dim c3 As String = NativeMe.RunMethod("openUSBCamera",  Array("1"))
   Log("Opening Camera 1 result :"&c3)
End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub


#If Java

import android.hardware.Camera;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CameraCharacteristics;
import java.util.List;
import java.util.ArrayList;
import android.view.Surface;
import android.graphics.SurfaceTexture;
import android.util.Size;
import android.widget.Toast;

import android.hardware.Camera.Parameters;
import android.hardware.Camera.CameraInfo;
import android.content.pm.PackageManager;
import android.content.Context;

CameraCaptureSession mSession;
CaptureRequest.Builder mBuilder;
CameraDevice mCameraDevice;
CameraManager mCameraManager;

public int camera1CameraCount() {
   try {
        int numberOfCameras = Camera.getNumberOfCameras();
       return numberOfCameras;
   } catch (Exception e) {
       return -1;
   }
}

public int camera2CameraCount() {
CameraManager manager;
manager=(CameraManager)getSystemService(Context.CAMERA_SERVICE);
  try {
       return manager.getCameraIdList().length;
  } catch (CameraAccessException e) {
    return -1;
  }
}

public String openUSBCamera(String CameraID) {
   CameraManager manager;
   manager=(CameraManager)getSystemService(Context.CAMERA_SERVICE);
   try {
       manager.openCamera(CameraID, new MyCameraDeviceStateCallback(), null);
       return "OK";
    } catch (Exception e) {
       Context context = getApplicationContext();
       String errorMessage = e.getMessage();
       Toast.makeText(context,errorMessage, Toast.LENGTH_SHORT).show();
        return "Fail";
    }  
}

    private Size getSmallestSize(String cameraId) throws CameraAccessException {
   /*
        Size[] outputSizes = mCameraManager.getCameraCharacteristics(cameraId)
                .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
                .getOutputSizes(SurfaceTexture.class);
        if (outputSizes == null || outputSizes.length == 0) {
            throw new IllegalStateException(
                    "Camera " + cameraId + "doesn't support any outputSize.");
        }
        Size chosen = outputSizes[0];
        for (Size s : outputSizes) {
            if (chosen.getWidth() >= s.getWidth() && chosen.getHeight() >= s.getHeight()) {
                chosen = s;
            }
        }
       */
       Size chosen = Size.parseSize("3*+6");
        return chosen;
    }
  
class MyCameraDeviceStateCallback extends CameraDevice.StateCallback {

    @Override
    public void onOpened(CameraDevice camera) {
        mCameraDevice = camera;
        //get builder
        try {
            mBuilder = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            //flash on, default is on
            mBuilder.set(CaptureRequest.CONTROL_AE_MODE, CameraMetadata.CONTROL_AF_MODE_AUTO);
            mBuilder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_TORCH);
            List<Surface> list = new ArrayList<Surface>();
            SurfaceTexture mSurfaceTexture = new SurfaceTexture(1);
            Size size = getSmallestSize(mCameraDevice.getId());
            mSurfaceTexture.setDefaultBufferSize(size.getWidth(), size.getHeight());
            Surface mSurface = new Surface(mSurfaceTexture);
            list.add(mSurface);
            mBuilder.addTarget(mSurface);
            camera.createCaptureSession(list, new MyCameraCaptureSessionStateCallback(), null);
        } catch (CameraAccessException e) {
           Context context = getApplicationContext();
           String errorMessage = e.getMessage();
           Toast.makeText(context,errorMessage, Toast.LENGTH_SHORT).show();          
        }
    }

    @Override
    public void onDisconnected(CameraDevice camera) {

    }

    @Override
    public void onError(CameraDevice camera, int error) {

    }
}

    class MyCameraCaptureSessionStateCallback extends CameraCaptureSession.StateCallback {

        @Override
        public void onConfigured(CameraCaptureSession session) {
            mSession = session;
            try {
                mSession.setRepeatingRequest(mBuilder.build(), null, null);
            } catch (CameraAccessException e) {
               Context context = getApplicationContext();
               String errorMessage = e.getMessage();
               Toast.makeText(context,errorMessage, Toast.LENGTH_SHORT).show();
            }
        }

        @Override
        public void onConfigureFailed(CameraCaptureSession session) {

        }
    }

#End If

(needs android.permission.CAMERA )

Here the output:
B4X:
Logger connected to:  asus ASUS_X00ID
--------- beginning of crash
--------- beginning of main
** Service (starter) Create **
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
With Camera1: 2
With Camera2: 2
USB devices: 1
USB 0: DeviceClass = 239
USB 0: DeviceId = 1010
USB 0: DeviceName = /dev/bus/usb/001/010
USB 0: DeviceSubclass = 2
USB 0: InterfaceCount = 11
USB 0: ProductId = 1885
USB 0: VendorId = 1118
Opening Camera USB 0 result :Fail
Opening Camera 0 result :OK
Opening Camera 1 result :OK
 
Last edited:
Upvote 0

Alessandra Pellegri

Active Member
Licensed User
Longtime User
Upvote 0

kanaida

Active Member
Licensed User
Longtime User
I will try this code on my generic 'X23' phone from China.
2 rear cameras, 1 front. No way to use 1 of the rear ones in it's out of the box software or other apps.
 
Upvote 0
Top