From ab48f9219b74853e8eba3122a1cfe7747ca2a6a9 Mon Sep 17 00:00:00 2001 From: Hardcore Sushi Date: Tue, 12 Apr 2022 16:09:56 +0200 Subject: [PATCH] CameraActivity: only bind 2 use cases at most + some other fixes --- app/build.gradle | 2 +- .../sushi/hardcore/droidfs/CameraActivity.kt | 89 ++++++++++++------- .../droidfs/video_recording/VideoCapture.java | 7 +- 3 files changed, 61 insertions(+), 37 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 46e0ec5..1a97cb2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,7 +76,7 @@ dependencies { implementation "androidx.concurrent:concurrent-futures:1.1.0" - def camerax_version = "1.1.0-beta02" + def camerax_version = "1.1.0-beta03" implementation "androidx.camera:camera-camera2:$camerax_version" implementation "androidx.camera:camera-lifecycle:$camerax_version" implementation "androidx.camera:camera-view:$camerax_version" diff --git a/app/src/main/java/sushi/hardcore/droidfs/CameraActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/CameraActivity.kt index 557cb50..e646c2b 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/CameraActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/CameraActivity.kt @@ -78,6 +78,7 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener { private var camera: Camera? = null private var resolutions: List? = null private var currentResolutionIndex: Int = 0 + private var currentResolution: Size? = null private var captureMode = ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY private var isBackCamera = true private var isInVideoMode = false @@ -139,7 +140,11 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener { if (newCaptureMode != captureMode) { captureMode = newCaptureMode binding.imageCaptureMode.setImageResource(resId) - setupCamera() + if (!isInVideoMode) { + cameraProvider.unbind(imageCapture) + refreshImageCapture() + cameraProvider.bindToLifecycle(this, cameraSelector, imageCapture) + } } dialog.dismiss() } @@ -151,9 +156,10 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener { CustomAlertDialogBuilder(this, themeValue) .setTitle(R.string.choose_resolution) .setSingleChoiceItems(it.map { size -> size.toString() }.toTypedArray(), currentResolutionIndex) { dialog, which -> - setupCamera(resolutions!![which]) - dialog.dismiss() + currentResolution = resolutions!![which] currentResolutionIndex = which + setupCamera() + dialog.dismiss() } .setNegativeButton(R.string.cancel, null) .show() @@ -204,6 +210,7 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener { } binding.imageModeSwitch.setOnClickListener { isInVideoMode = !isInVideoMode + setupCamera() binding.imageFlash.setImageResource(if (isInVideoMode) { binding.recordVideoButton.visibility = View.VISIBLE binding.takePhotoButton.visibility = View.GONE @@ -239,6 +246,7 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener { } true } + resolutions = null setupCamera() } binding.takePhotoButton.onClick = ::onClickTakePhoto @@ -301,52 +309,67 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener { private fun adaptPreviewSize(resolution: Size) { val screenWidth = resources.displayMetrics.widthPixels - binding.cameraPreview.layoutParams = if (screenWidth < resolution.width) { - RelativeLayout.LayoutParams( - screenWidth, - (resolution.height * (screenWidth.toFloat() / resolution.width)).toInt() - ) - } else { - RelativeLayout.LayoutParams(resolution.width, resolution.height) + val screenHeight = resources.displayMetrics.heightPixels + + var height = (resolution.height * (screenWidth.toFloat() / resolution.width)).toInt() + var width = screenWidth + if (height > screenHeight) { + width = (width * (screenHeight.toFloat() / height)).toInt() + height = screenHeight } - (binding.cameraPreview.layoutParams as RelativeLayout.LayoutParams).addRule(RelativeLayout.CENTER_IN_PARENT) + binding.cameraPreview.layoutParams = RelativeLayout.LayoutParams(width, height).apply { + addRule(RelativeLayout.CENTER_IN_PARENT) + } + } + + private fun refreshImageCapture() { + imageCapture = ImageCapture.Builder() + .setCaptureMode(captureMode) + .setFlashMode(imageCapture?.flashMode ?: ImageCapture.FLASH_MODE_AUTO) + .apply { + currentResolution?.let { + setTargetResolution(it) + } + } + .build() + } + + private fun refreshVideoCapture() { + videoCapture = VideoCapture.Builder().apply { + currentResolution?.let { + setTargetResolution(it) + } + }.build() } @SuppressLint("RestrictedApi") - private fun setupCamera(resolution: Size? = null){ + private fun setupCamera() { if (permissionsGranted && ::extensionsManager.isInitialized && ::cameraProvider.isInitialized) { - imageCapture = ImageCapture.Builder() - .setCaptureMode(captureMode) - .setFlashMode(imageCapture?.flashMode ?: ImageCapture.FLASH_MODE_AUTO) - .apply { - resolution?.let { - setTargetResolution(it) - } - } - .build() - - videoCapture = VideoCapture.Builder().apply { - resolution?.let { - setTargetResolution(it) - } - }.build() - cameraSelector = if (isBackCamera){ CameraSelector.DEFAULT_BACK_CAMERA } else { CameraSelector.DEFAULT_FRONT_CAMERA } - if (extensionsManager.isExtensionAvailable(cameraSelector, ExtensionMode.HDR)) { - cameraSelector = extensionsManager.getExtensionEnabledCameraSelector(cameraSelector, ExtensionMode.HDR) + if (extensionsManager.isExtensionAvailable(cameraSelector, ExtensionMode.AUTO)) { + cameraSelector = extensionsManager.getExtensionEnabledCameraSelector(cameraSelector, ExtensionMode.AUTO) } cameraProvider.unbindAll() - camera = cameraProvider.bindToLifecycle(this, cameraSelector, cameraPreview, imageCapture, videoCapture) - adaptPreviewSize(resolution ?: imageCapture!!.attachedSurfaceResolution!!.swap()) + val currentUseCase = (if (isInVideoMode) { + refreshVideoCapture() + camera = cameraProvider.bindToLifecycle(this, cameraSelector, cameraPreview, videoCapture) + videoCapture + } else { + refreshImageCapture() + camera = cameraProvider.bindToLifecycle(this, cameraSelector, cameraPreview, imageCapture) + imageCapture + })!! + + adaptPreviewSize(currentResolution ?: currentUseCase.attachedSurfaceResolution!!.swap()) if (resolutions == null) { val info = Camera2CameraInfo.from(camera!!.cameraInfo) val cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager val characteristics = cameraManager.getCameraCharacteristics(info.cameraId) characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)?.let { streamConfigurationMap -> - resolutions = streamConfigurationMap.getOutputSizes(imageCapture!!.imageFormat).map { it.swap() } + resolutions = streamConfigurationMap.getOutputSizes(currentUseCase.imageFormat).map { it.swap() } } } } diff --git a/app/src/main/java/sushi/hardcore/droidfs/video_recording/VideoCapture.java b/app/src/main/java/sushi/hardcore/droidfs/video_recording/VideoCapture.java index 22a6dbf..0361455 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/video_recording/VideoCapture.java +++ b/app/src/main/java/sushi/hardcore/droidfs/video_recording/VideoCapture.java @@ -41,7 +41,6 @@ import static androidx.camera.core.internal.ThreadConfig.OPTION_BACKGROUND_EXECU import static androidx.camera.core.internal.UseCaseEventConfig.OPTION_USE_CASE_EVENT_CALLBACK; import android.Manifest; -import android.annotation.SuppressLint; import android.media.AudioFormat; import android.media.AudioRecord; import android.media.CamcorderProfile; @@ -76,6 +75,7 @@ import androidx.annotation.VisibleForTesting; import androidx.camera.core.AspectRatio; import androidx.camera.core.CameraSelector; import androidx.camera.core.CameraXThreads; +import androidx.camera.core.ImageCapture; import androidx.camera.core.Logger; import androidx.camera.core.UseCase; import androidx.camera.core.impl.CameraInternal; @@ -121,7 +121,6 @@ import java.util.concurrent.atomic.AtomicReference; * @hide */ @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java -@SuppressLint("RestrictedApi") public final class VideoCapture extends UseCase { //////////////////////////////////////////////////////////////////////////////////////////// @@ -280,7 +279,9 @@ public final class VideoCapture extends UseCase { @Nullable public UseCaseConfig getDefaultConfig(boolean applyDefaultConfig, @NonNull UseCaseConfigFactory factory) { - Config captureConfig = factory.getConfig(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE); + Config captureConfig = factory.getConfig( + UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE, + ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY); if (applyDefaultConfig) { captureConfig = Config.mergeConfigs(captureConfig, DEFAULT_CONFIG.getConfig());