1 /* 2 * Copyright 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.camera.camera2.pipe.integration.impl 18 19 import android.hardware.camera2.CameraDevice 20 import androidx.camera.camera2.pipe.CameraGraph 21 import androidx.camera.camera2.pipe.GraphState.GraphStateError 22 import androidx.camera.camera2.pipe.GraphState.GraphStateStarted 23 import androidx.camera.camera2.pipe.GraphState.GraphStateStopped 24 import androidx.camera.camera2.pipe.core.Log.debug 25 import androidx.camera.camera2.pipe.integration.adapter.RequestProcessorAdapter 26 import androidx.camera.camera2.pipe.integration.adapter.SessionConfigAdapter 27 import androidx.camera.camera2.pipe.integration.config.UseCaseCameraScope 28 import androidx.camera.camera2.pipe.integration.config.UseCaseGraphConfig 29 import androidx.camera.core.ImageCapture 30 import androidx.camera.core.UseCase 31 import androidx.camera.core.imagecapture.CameraCapturePipeline 32 import androidx.camera.core.impl.Config 33 import androidx.camera.core.impl.SessionProcessorSurface 34 import dagger.Binds 35 import dagger.Module 36 import javax.inject.Inject 37 import kotlinx.atomicfu.atomic 38 import kotlinx.coroutines.CompletableDeferred 39 import kotlinx.coroutines.CoroutineStart 40 import kotlinx.coroutines.Job 41 import kotlinx.coroutines.flow.first 42 import kotlinx.coroutines.launch 43 44 internal val useCaseCameraIds = atomic(0) 45 internal val defaultOptionPriority = Config.OptionPriority.OPTIONAL 46 internal const val defaultTemplate = CameraDevice.TEMPLATE_PREVIEW 47 48 @JvmDefaultWithCompatibility 49 public interface UseCaseCamera { 50 // RequestControl of the UseCaseCamera 51 public val requestControl: UseCaseCameraRequestControl 52 getCameraCapturePipelinenull53 public suspend fun getCameraCapturePipeline( 54 @ImageCapture.CaptureMode captureMode: Int, 55 @ImageCapture.FlashMode flashMode: Int, 56 @ImageCapture.FlashType flashType: Int, 57 ): CameraCapturePipeline 58 59 public fun setActiveResumeMode(enabled: Boolean) {} 60 61 // Lifecycle closenull62 public fun close(): Job 63 } 64 65 /** API for interacting with a [CameraGraph] that has been configured with a set of [UseCase]'s */ 66 @UseCaseCameraScope 67 @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") 68 // Java version required for Dagger 69 public class UseCaseCameraImpl 70 @Inject 71 constructor( 72 private val useCaseGraphConfig: UseCaseGraphConfig, 73 private val useCases: java.util.ArrayList<UseCase>, 74 private val useCaseSurfaceManager: UseCaseSurfaceManager, 75 private val threads: UseCaseThreads, 76 private val sessionProcessorManager: SessionProcessorManager?, 77 private val sessionConfigAdapter: SessionConfigAdapter, 78 override val requestControl: UseCaseCameraRequestControl, 79 private val capturePipeline: CapturePipeline 80 ) : UseCaseCamera { 81 private val debugId = useCaseCameraIds.incrementAndGet() 82 private val closed = atomic(false) 83 84 init { 85 debug { "Configured $this for $useCases" } 86 useCaseGraphConfig.apply { cameraStateAdapter.onGraphUpdated(graph) } 87 threads.scope.launch { 88 useCaseGraphConfig.apply { 89 graph.graphState.collect { 90 cameraStateAdapter.onGraphStateUpdated(graph, it) 91 92 // Even if the UseCaseCamera is closed, we should still update the GraphState 93 // before cancelling the job, because it could be the last UseCaseCamera created 94 // (i.e., no new UseCaseCamera to update CameraStateAdapter that this one as 95 // stopped/closed). 96 if (closed.value && it is GraphStateStopped || it is GraphStateError) { 97 this@launch.coroutineContext[Job]?.cancel() 98 } 99 100 // TODO: b/323614735: Technically our RequestProcessor implementation could be 101 // given to the SessionProcessor through onCaptureSessionStart after the 102 // new set of configurations (CameraGraph) is created. However, this seems to 103 // be causing occasional SIGBUS on the Android platform level. Delaying this 104 // seems to be mitigating the issue, but does result in overhead in startup 105 // latencies. Move this back to UseCaseManager once we understand more about 106 // the situation. 107 if (sessionProcessorManager != null && it is GraphStateStarted) { 108 val sessionProcessorSurfaces = 109 sessionConfigAdapter.deferrableSurfaces.map { 110 it as SessionProcessorSurface 111 } 112 val requestProcessorAdapter = 113 RequestProcessorAdapter( 114 useCaseGraphConfig, 115 sessionProcessorSurfaces, 116 threads 117 ) 118 sessionProcessorManager.onCaptureSessionStart(requestProcessorAdapter) 119 } 120 } 121 } 122 } 123 } 124 125 override fun close(): Job { 126 return if (closed.compareAndSet(expect = false, update = true)) { 127 threads.scope.launch(start = CoroutineStart.UNDISPATCHED) { 128 debug { "Closing $this" } 129 requestControl.close() 130 sessionProcessorManager?.prepareClose() 131 useCaseGraphConfig.graph.close() 132 if (sessionProcessorManager != null) { 133 useCaseGraphConfig.graph.graphState.first { 134 it is GraphStateStopped || it is GraphStateError 135 } 136 sessionProcessorManager.close() 137 } 138 useCaseSurfaceManager.stopAsync().await() 139 } 140 } else { 141 CompletableDeferred(Unit) 142 } 143 } 144 145 override fun setActiveResumeMode(enabled: Boolean) { 146 useCaseGraphConfig.graph.isForeground = enabled 147 } 148 149 override fun toString(): String = "UseCaseCamera-$debugId" 150 151 override suspend fun getCameraCapturePipeline( 152 @ImageCapture.CaptureMode captureMode: Int, 153 @ImageCapture.FlashMode flashMode: Int, 154 @ImageCapture.FlashType flashType: Int, 155 ): CameraCapturePipeline = 156 capturePipeline.getCameraCapturePipeline(captureMode, flashMode, flashType) 157 158 @Module 159 public abstract class Bindings { 160 @UseCaseCameraScope 161 @Binds 162 public abstract fun provideUseCaseCamera(useCaseCamera: UseCaseCameraImpl): UseCaseCamera 163 } 164 } 165