Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 42 additions & 17 deletions app/src/main/java/com/samsung/android/scan3d/serv/CamEngine.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import android.hardware.camera2.CameraDevice
import android.hardware.camera2.CameraManager
import android.hardware.camera2.CaptureRequest
import android.hardware.camera2.TotalCaptureResult
import android.hardware.camera2.params.OutputConfiguration
import android.hardware.camera2.params.SessionConfiguration
import android.media.ImageReader
import android.media.MediaCodec
import android.media.MediaCodecInfo
Expand All @@ -32,6 +34,7 @@ import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicInteger

class CamEngine(val context: Context) {

var http: HttpService? = null
Expand Down Expand Up @@ -86,7 +89,7 @@ class CamEngine(val context: Context) {
var viewState: CameraFragment.Companion.ViewState = CameraFragment.Companion.ViewState(
true,
stream = false,
cameraId = "0",
cameraId = cameraList.first().cameraId,
quality = 80,
resolutionIndex = null
)
Expand Down Expand Up @@ -133,14 +136,16 @@ class CamEngine(val context: Context) {
private suspend fun openCamera(
manager: CameraManager,
cameraId: String,
logicalCameraId: String?,
handler: Handler? = null
): CameraDevice = suspendCancellableCoroutine { cont ->
manager.openCamera(cameraId, object : CameraDevice.StateCallback() {

val idToOpen = logicalCameraId ?: cameraId;
manager.openCamera(idToOpen, object : CameraDevice.StateCallback() {
override fun onOpened(device: CameraDevice) = cont.resume(device)

override fun onDisconnected(device: CameraDevice) {
Log.w("CamEngine", "Camera $cameraId has been disconnected")

}

override fun onError(device: CameraDevice, error: Int) {
Expand All @@ -165,22 +170,39 @@ class CamEngine(val context: Context) {
*/
private suspend fun createCaptureSession(
device: CameraDevice,
physicalCamId: String?,
targets: List<Surface>,
handler: Handler? = null
): CameraCaptureSession = suspendCoroutine { cont ->
val outputConfigs = mutableListOf<OutputConfiguration>();
targets.forEach {
outputConfigs.add(OutputConfiguration(it).apply {
// If physical camera id is not null, it's a logical cam, you should set it
if (physicalCamId != null) {
setPhysicalCameraId(physicalCamId)
}
})
}

// Create a capture session using the predefined targets; this also involves defining the
// session state callback to be notified of when the session is ready
device.createCaptureSession(targets, object : CameraCaptureSession.StateCallback() {

override fun onConfigured(session: CameraCaptureSession) = cont.resume(session)

override fun onConfigureFailed(session: CameraCaptureSession) {
val exc = RuntimeException("Camera ${device.id} session configuration failed")
Log.e("CamEngine", exc.message, exc)
cont.resumeWithException(exc)
val sessionConfiguration = SessionConfiguration(
SessionConfiguration.SESSION_REGULAR,
outputConfigs,
Executors.newSingleThreadExecutor(),
object : CameraCaptureSession.StateCallback() {

override fun onConfigured(session: CameraCaptureSession) = cont.resume(session)

override fun onConfigureFailed(session: CameraCaptureSession) {
val exc = RuntimeException("Camera ${device.id} session configuration failed")
Log.e("CamEngine", exc.message, exc)
cont.resumeWithException(exc)
}
}
}, handler)
)

device.createCaptureSession(sessionConfiguration)
}

suspend fun initializeCamera() {
Expand Down Expand Up @@ -212,18 +234,21 @@ class CamEngine(val context: Context) {
resH = sizes[viewState.resolutionIndex!!].height



camera = openCamera(cameraManager, viewState.cameraId, cameraHandler)
val sensor = cameraList.find { it.cameraId == viewState.cameraId }!!
camera = openCamera(cameraManager, sensor.cameraId, sensor.logicalCameraId, cameraHandler)
imageReader = ImageReader.newInstance(
resW, resH, camOutPutFormat, 4
)
var targets = listOf(imageReader.surface)
if (showLiveSurface) {


targets = targets.plus(previewSurface!!)
}
session = createCaptureSession(camera, targets, cameraHandler)
session = createCaptureSession(
camera,
if (sensor.logicalCameraId == null) null else sensor.cameraId,
targets,
cameraHandler
)
val captureRequest = camera.createCaptureRequest(
CameraDevice.TEMPLATE_RECORD //TEMPLATE_PREVIEW
)
Expand Down
74 changes: 43 additions & 31 deletions app/src/main/java/com/samsung/android/scan3d/util/Selector.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ import kotlin.math.roundToInt
object Selector {
/** Helper class used as a data holder for each selectable camera format item */
@Parcelize
data class SensorDesc(val title: String, val cameraId: String, val format: Int) : Parcelable
data class SensorDesc(
val title: String,
val cameraId: String,
val logicalCameraId: String?,
val format: Int
) : Parcelable

/** Helper function used to convert a lens orientation enum into a human-readable string */
private fun lensOrientationString(value: Int) = when (value) {
Expand Down Expand Up @@ -66,51 +71,57 @@ object Selector {
// Get list of all compatible cameras
val cameraIds = mutableListOf<String>()

for (i in 0..100) {

val istr = i.toString()
if (!cameraIds.contains(istr)) {
cameraIds.add(istr)
}
cameraManager.cameraIdList.forEach { it ->
cameraIds.add(it.toString())
}

val cameraIds2 = mutableListOf<String>()
cameraIds.filter {
val cameraIds2 = mutableListOf<SensorDesc>()
val openableCameraIds = mutableListOf<String>()

cameraIds.forEach { id ->
try {
val characteristics = cameraManager.getCameraCharacteristics(it)
val characteristics = cameraManager.getCameraCharacteristics(id)
val capabilities = characteristics.get(
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES
)
) ?: return@forEach

if (capabilities == null) {
false
} else if (capabilities.contains(
if (capabilities.contains(
CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
)
) {
false
} else if (capabilities.contains(
CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
)
) {
true
// We got evil logical camera here, split it up into physical ones
characteristics.physicalCameraIds.forEach { physId ->
cameraIds2.add(SensorDesc("", physId, id, 0))
}
} else {
false
cameraIds2.add(SensorDesc("", id, null, 0))
openableCameraIds.add(id)
}
} catch (_: Exception) {


} catch (e: Exception) {
false
}
}.forEach { cameraIds2.add(it) }

// There can be physical cameras that you can access both from a logical camera and as a
// physical camera. (For example, on Galaxy S23 Ultra, the ultra-wide camera)
// We remove the duplicates here.
// We enumerate all directly openable camera IDs, then search in cameraIds2 for a logical
// camera entry that contains it, then delete it
val removeList = mutableListOf<SensorDesc>()
openableCameraIds.forEach { id ->
cameraIds2.removeAll {
it.logicalCameraId != null && it.cameraId == id
}
}

// Iterate over the list of cameras and return all the compatible ones
cameraIds2.forEach { id ->
cameraIds2.forEach { desc ->

Log.i("SELECTOR", "id: " + id)
val characteristics = cameraManager.getCameraCharacteristics(id)
Log.i("SELECTOR", "Camera ${desc.cameraId} @ LogicalCam ${desc.logicalCameraId}")
val characteristics = cameraManager.getCameraCharacteristics(desc.cameraId)
val orientation = lensOrientationString(
characteristics.get(CameraCharacteristics.LENS_FACING)!!
)
Expand All @@ -122,7 +133,6 @@ object Selector {

capabilities.forEach { Log.i("CAP", "" + getCapStringAtIndex(it)) }


val outputFormats = characteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP
)!!.outputFormats
Expand All @@ -148,17 +158,19 @@ object Selector {

// All cameras *must* support JPEG output so we don't need to check characteristics

val title= "vfov:$vfov $foc $ape $orientation"
if(!availableCameras.any {it-> it.title==title } ){
val camId = if (desc.logicalCameraId == null)
"${desc.cameraId}"
else
"${desc.cameraId}@${desc.logicalCameraId}"

val title = "$camId vfov:$vfov $foc $ape $orientation"
if (!availableCameras.any { it -> it.title == title }) {
availableCameras.add(
SensorDesc(
title, id, ImageFormat.JPEG
title, desc.cameraId, desc.logicalCameraId, ImageFormat.JPEG
)
)
}



}

return availableCameras
Expand Down