I have an external camera that's connected via a USB C dongle to my Android tablet. My goal is to have a constant stream of data from the camera into my phone, showing it to the user and allowing him to record it and save it to the local storage.
I am following the official docs from the following link - https://developer.android.com/guide/topics/connectivity/usb/host#working-d
And I have spent the last couple of hours trying to figure out how things work, mapping the interfaces and endpoints, eventually finding an interface that has an endpoint that when I call bulkTransfer()
on, does not return a failed value (-1).
I currently am facing 2 issues:
I have indeed got a valid response from the bulkTransfer()
function, but my ByteArray does not fill with relevant information - when trying to print out the values they are all 0's. I though it may be a wrong endpoint as suggested in the official docs, but I have tried all combinations of interfaces and endpoints until I get an indexOutOfBoundException. That combination of interface + endpoint that I used is the only one that produced a valid bulk response. What am I missing?
I am looking for a stream of data that doesn't stop, but it seems like when calling bulkTransfer()
it's one a one time oppression, unlike CameraX library for example that I get a constant callback each time a new chunck of data is available.
Here is the code on my main screen -
LaunchedEffect(key1 = true) {
val usbManager = context.getSystemService(Context.USB_SERVICE) as UsbManager
val filter = IntentFilter(ACTION_USB_PERMISSION)
registerReceiver(context, UsbBroadcastReceiver(), filter, RECEIVER_NOT_EXPORTED)
val hdCamera = usbManager.deviceList.values.find { device ->
val name = device.productName ?: return@LaunchedEffect
name.contains("HD camera")
} ?: return@LaunchedEffect
val permissionIntent = PendingIntent.getBroadcast(
context,
0, Intent(ACTION_USB_PERMISSION),
0
)
usbManager.requestPermission(hdCamera, permissionIntent)
}
And here is my BroadcastReceiver -
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action != ACTION_USB_PERMISSION) return
synchronized(this) {
val usbManager = context?.getSystemService(Context.USB_SERVICE) as UsbManager
val device: UsbDevice? = intent.getParcelable(UsbManager.EXTRA_DEVICE)
val usbInterface = device?.getInterface(0)
val endpoint = usbInterface?.getEndpoint(1) ?: return@synchronized
usbManager.openDevice(device)?.apply {
val array = ByteArray(endpoint.maxPacketSize)
claimInterface(usbInterface, true)
val bulkTransfer = bulkTransfer(endpoint, array, array.size, 0)
Log.d("defaultAppDebuger", "bulk array: $bulkTransfer") //prints a valid number - 512
array.forEach {
Log.d("defaultAppDebuger", "bulk array: $it") //the array values are empty
}
}
}
}
edit: I have tried to move the BroadcastReceiver code to an async coroutine thinking that the loading of the information is related to the fact that I am in the wrong thread. Still didn't work, I get a valid result from the bulkTransfer and the byteArray is not filled -
fun BroadcastReceiver.goAsync(
context: CoroutineContext = Dispatchers.IO,
block: suspend CoroutineScope.() -> Unit
) {
val pendingResult = goAsync()
CoroutineScope(SupervisorJob()).launch(context) {
try {
block()
} finally {
pendingResult.finish()
}
}
}
override fun onReceive(context: Context?, intent: Intent?) = goAsync { .... }
Thanks!
After carefully researching I was not able to get an answer and ditched that mini project that I worked on. I followed this comment on the following thread -
https://stackoverflow.com/a/68120774/8943516
That, combined with a 2.5 days of deep researched of both USB Host protocol which was not able to connect to my camera and Camera2API which couldn't recognize my external camera brought me to a dead end.