feat: Implement focus() on Android (#2523)

* feat: Implement `focus()` on Android

* Throw if not supported

* Do focus in example

* Format

* fix: Properly convert layer point to camera coordinates

* Fix

* Set trigger back to IDLE

* Fix rotation maybe?

* Rotated by

* fix: Fix display point calculation

* Try other

* Invoke `capture` callback on same thread

* Center metering rectangle

* Reset AF Trigger to IDLE

* Reset it to it's default AF mode again, i dont even know anymore

* Update CameraPage.tsx

* Format

* Apply options to repeating

* Set

* Use scene mode

* Update CameraPage.tsx

* Update CameraDeviceDetails.kt

* It fucking works

* Update PersistentCameraCaptureSession.kt

* Update PersistentCameraCaptureSession.kt

* Update PersistentCameraCaptureSession.kt

* Create CameraCaptureSession+setRepeatingRequestAndWait.kt

* Oh my god it works

* Also focus AE

* Cancel reset request

* Rename to AF

* Format

* Update PersistentCameraCaptureSession.kt
This commit is contained in:
Marc Rousavy
2024-02-08 15:16:58 +01:00
committed by GitHub
parent fce6616964
commit fb1d82ad9a
14 changed files with 288 additions and 33 deletions

View File

@@ -5,7 +5,6 @@ import android.hardware.camera2.CaptureFailure
import android.hardware.camera2.CaptureRequest
import android.hardware.camera2.TotalCaptureResult
import android.media.MediaActionSound
import com.mrousavy.camera.core.CameraQueues
import com.mrousavy.camera.core.CaptureAbortedError
import com.mrousavy.camera.core.UnknownCaptureError
import kotlin.coroutines.resume
@@ -23,29 +22,36 @@ suspend fun CameraCaptureSession.capture(captureRequest: CaptureRequest, enableS
override fun onCaptureCompleted(session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult) {
super.onCaptureCompleted(session, request, result)
continuation.resume(result)
shutterSound?.release()
if (request == captureRequest) {
continuation.resume(result)
shutterSound?.release()
}
}
override fun onCaptureStarted(session: CameraCaptureSession, request: CaptureRequest, timestamp: Long, frameNumber: Long) {
super.onCaptureStarted(session, request, timestamp, frameNumber)
if (enableShutterSound) {
shutterSound?.play(MediaActionSound.SHUTTER_CLICK)
if (request == captureRequest) {
if (enableShutterSound) {
shutterSound?.play(MediaActionSound.SHUTTER_CLICK)
}
}
}
override fun onCaptureFailed(session: CameraCaptureSession, request: CaptureRequest, failure: CaptureFailure) {
super.onCaptureFailed(session, request, failure)
val wasImageCaptured = failure.wasImageCaptured()
val error = when (failure.reason) {
CaptureFailure.REASON_ERROR -> UnknownCaptureError(wasImageCaptured)
CaptureFailure.REASON_FLUSHED -> CaptureAbortedError(wasImageCaptured)
else -> UnknownCaptureError(wasImageCaptured)
if (request == captureRequest) {
val wasImageCaptured = failure.wasImageCaptured()
val error = when (failure.reason) {
CaptureFailure.REASON_ERROR -> UnknownCaptureError(wasImageCaptured)
CaptureFailure.REASON_FLUSHED -> CaptureAbortedError(wasImageCaptured)
else -> UnknownCaptureError(wasImageCaptured)
}
continuation.resumeWithException(error)
}
continuation.resumeWithException(error)
}
},
CameraQueues.cameraQueue.handler
null
)
}

View File

@@ -0,0 +1,47 @@
package com.mrousavy.camera.extensions
import android.hardware.camera2.CameraCaptureSession
import android.hardware.camera2.CaptureFailure
import android.hardware.camera2.CaptureRequest
import android.hardware.camera2.CaptureResult
import android.hardware.camera2.TotalCaptureResult
import android.util.Log
import com.mrousavy.camera.core.CaptureAbortedError
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlinx.coroutines.suspendCancellableCoroutine
private const val TAG = "CameraCaptureSession"
/**
* Set a new repeating request for the [CameraCaptureSession] that contains an AF trigger, and wait until AF has locked.
*/
suspend fun CameraCaptureSession.setRepeatingRequestAndWaitForAF(request: CaptureRequest) =
suspendCancellableCoroutine { continuation ->
this.setRepeatingRequest(
request,
object : CameraCaptureSession.CaptureCallback() {
override fun onCaptureCompleted(session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult) {
super.onCaptureCompleted(session, request, result)
if (continuation.isActive) {
val afState = result.get(CaptureResult.CONTROL_AF_STATE)
Log.i(TAG, "AF State: $afState")
if (afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED || afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) {
continuation.resume(Unit)
session.setRepeatingRequest(request, null, null)
}
}
}
override fun onCaptureFailed(session: CameraCaptureSession, request: CaptureRequest, failure: CaptureFailure) {
super.onCaptureFailed(session, request, failure)
if (continuation.isActive) {
continuation.resumeWithException(CaptureAbortedError(failure.wasImageCaptured()))
session.setRepeatingRequest(request, null, null)
}
}
},
null
)
}

View File

@@ -2,7 +2,7 @@ package com.mrousavy.camera.extensions
import android.util.Size
import android.util.SizeF
import android.view.Surface
import com.mrousavy.camera.types.Orientation
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
@@ -14,13 +14,10 @@ fun List<Size>.closestToOrMax(size: Size?): Size =
this.maxBy { it.width * it.height }
}
fun Size.rotated(surfaceRotation: Int): Size =
when (surfaceRotation) {
Surface.ROTATION_0 -> Size(width, height)
Surface.ROTATION_90 -> Size(height, width)
Surface.ROTATION_180 -> Size(width, height)
Surface.ROTATION_270 -> Size(height, width)
else -> Size(width, height)
fun Size.rotatedBy(orientation: Orientation): Size =
when (orientation) {
Orientation.PORTRAIT, Orientation.PORTRAIT_UPSIDE_DOWN -> this
Orientation.LANDSCAPE_LEFT, Orientation.LANDSCAPE_RIGHT -> Size(height, width)
}
val Size.bigger: Int

View File

@@ -1,5 +1,6 @@
package com.mrousavy.camera.extensions
import android.util.Log
import android.view.SurfaceHolder
import androidx.annotation.UiThread
import kotlin.coroutines.resume
@@ -15,14 +16,18 @@ suspend fun SurfaceHolder.resize(width: Int, height: Int) {
return@suspendCancellableCoroutine
}
Log.i("SurfaceHolder", "Resizing SurfaceHolder to $width x $height...")
val callback = object : SurfaceHolder.Callback {
override fun surfaceCreated(holder: SurfaceHolder) = Unit
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
holder.removeCallback(this)
Log.i("SurfaceHolder", "Resized SurfaceHolder to $width x $height!")
continuation.resume(Unit)
}
override fun surfaceDestroyed(holder: SurfaceHolder) {
holder.removeCallback(this)
Log.e("SurfaceHolder", "Failed to resize SurfaceHolder to $width x $height!")
continuation.cancel(Error("Tried to resize SurfaceView, but Surface has been destroyed!"))
}
}