fix: (Android) Give real video resolutions, unbind/rebind preview in onHostResume, add missing Android capture errors (#1079)
* Calculate a format's video dimensions based on supported resolutions and photo dimensions * Add Android fallback strategy for recording quality * Ensure that session props are not ignored when app is resumed * Simplify setting Android video dimensions in format * Modify Android imageAnalysisBuilder to use photoSize * Update onHostResume function to reference android preview issue * Add missing Android capture errors
This commit is contained in:
parent
fb2156ec39
commit
096b1cca4e
@ -235,6 +235,8 @@ class CameraView(context: Context, private val frameProcessorThread: ExecutorSer
|
|||||||
override fun onHostResume() {
|
override fun onHostResume() {
|
||||||
hostLifecycleState = Lifecycle.State.RESUMED
|
hostLifecycleState = Lifecycle.State.RESUMED
|
||||||
updateLifecycleState()
|
updateLifecycleState()
|
||||||
|
// workaround for https://issuetracker.google.com/issues/147354615, preview must be bound on resume
|
||||||
|
update(propsThatRequireSessionReconfiguration)
|
||||||
}
|
}
|
||||||
override fun onHostPause() {
|
override fun onHostPause() {
|
||||||
hostLifecycleState = Lifecycle.State.CREATED
|
hostLifecycleState = Lifecycle.State.CREATED
|
||||||
@ -410,16 +412,21 @@ class CameraView(context: Context, private val frameProcessorThread: ExecutorSer
|
|||||||
// User has selected a custom format={}. Use that
|
// User has selected a custom format={}. Use that
|
||||||
val format = DeviceFormat(format!!)
|
val format = DeviceFormat(format!!)
|
||||||
Log.i(TAG, "Using custom format - photo: ${format.photoSize}, video: ${format.videoSize} @ $fps FPS")
|
Log.i(TAG, "Using custom format - photo: ${format.photoSize}, video: ${format.videoSize} @ $fps FPS")
|
||||||
|
if (video == true) {
|
||||||
previewBuilder.setTargetResolution(format.videoSize)
|
previewBuilder.setTargetResolution(format.videoSize)
|
||||||
|
} else {
|
||||||
|
previewBuilder.setTargetResolution(format.photoSize)
|
||||||
|
}
|
||||||
imageCaptureBuilder.setTargetResolution(format.photoSize)
|
imageCaptureBuilder.setTargetResolution(format.photoSize)
|
||||||
imageAnalysisBuilder.setTargetResolution(format.videoSize)
|
imageAnalysisBuilder.setTargetResolution(format.photoSize)
|
||||||
|
|
||||||
// TODO: Ability to select resolution exactly depending on format? Just like on iOS...
|
// TODO: Ability to select resolution exactly depending on format? Just like on iOS...
|
||||||
when (min(format.videoSize.height, format.videoSize.width)) {
|
when (min(format.videoSize.height, format.videoSize.width)) {
|
||||||
in 0..480 -> videoRecorderBuilder.setQualitySelector(QualitySelector.from(Quality.SD))
|
in 0..480 -> videoRecorderBuilder.setQualitySelector(QualitySelector.from(Quality.SD))
|
||||||
in 480..720 -> videoRecorderBuilder.setQualitySelector(QualitySelector.from(Quality.HD))
|
in 480..720 -> videoRecorderBuilder.setQualitySelector(QualitySelector.from(Quality.HD, FallbackStrategy.lowerQualityThan(Quality.HD)))
|
||||||
in 720..1080 -> videoRecorderBuilder.setQualitySelector(QualitySelector.from(Quality.FHD))
|
in 720..1080 -> videoRecorderBuilder.setQualitySelector(QualitySelector.from(Quality.FHD, FallbackStrategy.lowerQualityThan(Quality.FHD)))
|
||||||
in 1080..2160 -> videoRecorderBuilder.setQualitySelector(QualitySelector.from(Quality.UHD))
|
in 1080..2160 -> videoRecorderBuilder.setQualitySelector(QualitySelector.from(Quality.UHD, FallbackStrategy.lowerQualityThan(Quality.UHD)))
|
||||||
|
in 2160..4320 -> videoRecorderBuilder.setQualitySelector(QualitySelector.from(Quality.HIGHEST, FallbackStrategy.lowerQualityThan(Quality.HIGHEST)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fps?.let { fps ->
|
fps?.let { fps ->
|
||||||
|
@ -7,10 +7,12 @@ import android.hardware.camera2.CameraCharacteristics
|
|||||||
import android.hardware.camera2.CameraManager
|
import android.hardware.camera2.CameraManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.util.Size
|
||||||
import androidx.camera.core.CameraSelector
|
import androidx.camera.core.CameraSelector
|
||||||
import androidx.camera.extensions.ExtensionMode
|
import androidx.camera.extensions.ExtensionMode
|
||||||
import androidx.camera.extensions.ExtensionsManager
|
import androidx.camera.extensions.ExtensionsManager
|
||||||
import androidx.camera.lifecycle.ProcessCameraProvider
|
import androidx.camera.lifecycle.ProcessCameraProvider
|
||||||
|
import androidx.camera.video.QualitySelector
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.facebook.react.bridge.*
|
import com.facebook.react.bridge.*
|
||||||
import com.facebook.react.module.annotations.ReactModule
|
import com.facebook.react.module.annotations.ReactModule
|
||||||
@ -227,6 +229,16 @@ class CameraViewModule(reactContext: ReactApplicationContext) : ReactContextBase
|
|||||||
}
|
}
|
||||||
map.putDouble("neutralZoom", 1.0)
|
map.putDouble("neutralZoom", 1.0)
|
||||||
|
|
||||||
|
val supportedVideoResolutions: List<Size>
|
||||||
|
val cameraInfos = cameraSelector.filter(cameraProvider.availableCameraInfos)
|
||||||
|
if (cameraInfos.size > 0) {
|
||||||
|
supportedVideoResolutions = QualitySelector
|
||||||
|
.getSupportedQualities(cameraInfos[0])
|
||||||
|
.map { QualitySelector.getResolution(cameraInfos[0], it)!! }
|
||||||
|
} else {
|
||||||
|
supportedVideoResolutions = emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Optimize?
|
// TODO: Optimize?
|
||||||
val maxImageOutputSize = cameraConfig.outputFormats
|
val maxImageOutputSize = cameraConfig.outputFormats
|
||||||
.flatMap { cameraConfig.getOutputSizes(it).toList() }
|
.flatMap { cameraConfig.getOutputSizes(it).toList() }
|
||||||
@ -234,7 +246,6 @@ class CameraViewModule(reactContext: ReactApplicationContext) : ReactContextBase
|
|||||||
|
|
||||||
val formats = Arguments.createArray()
|
val formats = Arguments.createArray()
|
||||||
|
|
||||||
// TODO: Get supported video qualities with QualitySelector.getSupportedQualities(...)
|
|
||||||
cameraConfig.outputFormats.forEach { formatId ->
|
cameraConfig.outputFormats.forEach { formatId ->
|
||||||
val formatName = parseImageFormat(formatId)
|
val formatName = parseImageFormat(formatId)
|
||||||
|
|
||||||
@ -287,8 +298,12 @@ class CameraViewModule(reactContext: ReactApplicationContext) : ReactContextBase
|
|||||||
val format = Arguments.createMap()
|
val format = Arguments.createMap()
|
||||||
format.putDouble("photoHeight", size.height.toDouble())
|
format.putDouble("photoHeight", size.height.toDouble())
|
||||||
format.putDouble("photoWidth", size.width.toDouble())
|
format.putDouble("photoWidth", size.width.toDouble())
|
||||||
format.putDouble("videoHeight", size.height.toDouble()) // TODO: Revisit getAvailableCameraDevices (videoHeight == photoHeight?)
|
// since supportedVideoResolutions is sorted from highest resolution to lowest,
|
||||||
format.putDouble("videoWidth", size.width.toDouble()) // TODO: Revisit getAvailableCameraDevices (videoWidth == photoWidth?)
|
// videoResolution will be the highest supported video resolution lower than or equal to photo resolution
|
||||||
|
// TODO: Somehow integrate with CamcorderProfileProxy?
|
||||||
|
val videoResolution = supportedVideoResolutions.find { it.width <= size.width && it.height <= size.height }
|
||||||
|
format.putDouble("videoHeight", videoResolution?.height?.toDouble())
|
||||||
|
format.putDouble("videoWidth", videoResolution?.width?.toDouble())
|
||||||
format.putBoolean("isHighestPhotoQualitySupported", isHighestPhotoQualitySupported)
|
format.putBoolean("isHighestPhotoQualitySupported", isHighestPhotoQualitySupported)
|
||||||
format.putInt("maxISO", isoRange?.upper)
|
format.putInt("maxISO", isoRange?.upper)
|
||||||
format.putInt("minISO", isoRange?.lower)
|
format.putInt("minISO", isoRange?.lower)
|
||||||
|
@ -36,7 +36,13 @@ export type CaptureError =
|
|||||||
| 'capture/no-recording-in-progress'
|
| 'capture/no-recording-in-progress'
|
||||||
| 'capture/file-io-error'
|
| 'capture/file-io-error'
|
||||||
| 'capture/create-temp-file-error'
|
| 'capture/create-temp-file-error'
|
||||||
|
| 'capture/invalid-video-options'
|
||||||
| 'capture/create-recorder-error'
|
| 'capture/create-recorder-error'
|
||||||
|
| 'capture/recorder-error'
|
||||||
|
| 'capture/no-valid-data'
|
||||||
|
| 'capture/inactive-source'
|
||||||
|
| 'capture/insufficient-storage'
|
||||||
|
| 'capture/file-size-limit-reached'
|
||||||
| 'capture/invalid-photo-codec'
|
| 'capture/invalid-photo-codec'
|
||||||
| 'capture/not-bound-error'
|
| 'capture/not-bound-error'
|
||||||
| 'capture/capture-type-not-supported'
|
| 'capture/capture-type-not-supported'
|
||||||
|
Loading…
Reference in New Issue
Block a user