1 /*
2  * Copyright 2024 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.graph
18 
19 import androidx.camera.camera2.pipe.FrameInfo
20 import androidx.camera.camera2.pipe.FrameNumber
21 import androidx.camera.camera2.pipe.Request
22 import androidx.camera.camera2.pipe.RequestMetadata
23 import androidx.camera.camera2.pipe.core.Log
24 import kotlinx.atomicfu.atomic
25 import kotlinx.atomicfu.update
26 import kotlinx.atomicfu.updateAndGet
27 
28 /**
29  * On some devices, we need to wait for 10 frames to complete before we can guarantee the success of
30  * single capture requests. This is a quirk identified as part of b/287020251 and reported in
31  * b/289284907.
32  *
33  * During initialization, setting the graphLoop will disableCaptureProcessing until after the
34  * required number of frames have been completed.
35  */
36 internal class CaptureLimiter(private val requestsUntilActive: Long) :
37     Request.Listener, GraphLoop.Listener {
38     init {
39         require(requestsUntilActive > 0)
40     }
41 
42     private val frameCount = atomic(0L)
43     private var _graphLoop: GraphLoop? = null
44     var graphLoop: GraphLoop
45         get() = _graphLoop!!
46         set(value) {
<lambda>null47             check(_graphLoop == null) { "GraphLoop has already been set!" }
48             _graphLoop = value
49             value.captureProcessingEnabled = false
<lambda>null50             Log.warn {
51                 "Capture processing has been disabled for $value until $requestsUntilActive " +
52                     "frames have been completed."
53             }
54         }
55 
onCompletenull56     override fun onComplete(
57         requestMetadata: RequestMetadata,
58         frameNumber: FrameNumber,
59         result: FrameInfo
60     ) {
61         val count = frameCount.updateAndGet { if (it == -1L) -1 else it + 1 }
62         if (count == requestsUntilActive) {
63             Log.warn { "Capture processing is now enabled for $_graphLoop after $count frames." }
64             graphLoop.captureProcessingEnabled = true
65         }
66     }
67 
onStopRepeatingnull68     override fun onStopRepeating() {
69         // Ignored
70     }
71 
onGraphStoppednull72     override fun onGraphStopped() {
73         // If the cameraGraph is stopped, reset the counter
74         frameCount.update { if (it == -1L) -1 else 0 }
75         graphLoop.captureProcessingEnabled = false
76         Log.warn {
77             "Capture processing has been disabled for $graphLoop until $requestsUntilActive " +
78                 "frames have been completed."
79         }
80     }
81 
onGraphShutdownnull82     override fun onGraphShutdown() {
83         frameCount.value = -1
84         graphLoop.captureProcessingEnabled = false
85     }
86 }
87