1 /*
<lambda>null2  * Copyright 2022 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.extensions.internal
18 
19 import android.content.Context
20 import android.graphics.ImageFormat
21 import android.graphics.SurfaceTexture
22 import android.hardware.camera2.CameraCharacteristics
23 import android.hardware.camera2.CameraDevice
24 import android.hardware.camera2.CameraExtensionCharacteristics.EXTENSION_FACE_RETOUCH
25 import android.hardware.camera2.CaptureFailure
26 import android.hardware.camera2.CaptureRequest
27 import android.hardware.camera2.CaptureResult
28 import android.hardware.camera2.TotalCaptureResult
29 import android.hardware.camera2.params.OutputConfiguration
30 import android.hardware.camera2.params.SessionConfiguration
31 import android.media.ImageReader
32 import android.media.ImageWriter
33 import android.os.Build
34 import android.util.Pair
35 import android.util.Range
36 import android.util.Size
37 import android.view.Surface
38 import androidx.annotation.RequiresApi
39 import androidx.camera.camera2.Camera2Config
40 import androidx.camera.core.CameraFilter
41 import androidx.camera.core.CameraInfo
42 import androidx.camera.core.CameraSelector
43 import androidx.camera.core.ImageAnalysis
44 import androidx.camera.core.ImageCapture
45 import androidx.camera.core.ImageCapture.OnImageCapturedCallback
46 import androidx.camera.core.ImageProxy
47 import androidx.camera.core.Preview
48 import androidx.camera.core.UseCaseGroup
49 import androidx.camera.core.impl.CameraCaptureResult
50 import androidx.camera.core.impl.CameraConfig
51 import androidx.camera.core.impl.CameraInfoInternal
52 import androidx.camera.core.impl.Config
53 import androidx.camera.core.impl.ExtendedCameraConfigProviderStore
54 import androidx.camera.core.impl.Identifier
55 import androidx.camera.core.impl.MutableOptionsBundle
56 import androidx.camera.core.impl.MutableTagBundle
57 import androidx.camera.core.impl.OutputSurface
58 import androidx.camera.core.impl.OutputSurfaceConfiguration
59 import androidx.camera.core.impl.SessionProcessor
60 import androidx.camera.core.impl.TagBundle
61 import androidx.camera.core.impl.utils.executor.CameraXExecutors
62 import androidx.camera.extensions.ExtensionMode
63 import androidx.camera.extensions.impl.advanced.AdvancedExtenderImpl
64 import androidx.camera.extensions.impl.advanced.Camera2OutputConfigImpl
65 import androidx.camera.extensions.impl.advanced.Camera2OutputConfigImplBuilder
66 import androidx.camera.extensions.impl.advanced.Camera2SessionConfigImpl
67 import androidx.camera.extensions.impl.advanced.OutputSurfaceConfigurationImpl
68 import androidx.camera.extensions.impl.advanced.OutputSurfaceImpl
69 import androidx.camera.extensions.impl.advanced.RequestProcessorImpl
70 import androidx.camera.extensions.impl.advanced.SessionProcessorImpl
71 import androidx.camera.extensions.internal.sessionprocessor.AdvancedSessionProcessor
72 import androidx.camera.extensions.util.Camera2SessionConfigImplBuilder
73 import androidx.camera.lifecycle.ProcessCameraProvider
74 import androidx.camera.testing.fakes.FakeCameraInfoInternal
75 import androidx.camera.testing.impl.CameraUtil
76 import androidx.camera.testing.impl.SurfaceTextureProvider
77 import androidx.camera.testing.impl.SurfaceTextureProvider.SurfaceTextureCallback
78 import androidx.camera.testing.impl.fakes.FakeLifecycleOwner
79 import androidx.test.core.app.ApplicationProvider
80 import androidx.test.ext.junit.runners.AndroidJUnit4
81 import androidx.test.filters.LargeTest
82 import androidx.test.filters.SdkSuppress
83 import com.google.common.truth.Truth.assertThat
84 import java.util.concurrent.CountDownLatch
85 import java.util.concurrent.TimeUnit
86 import kotlinx.coroutines.CompletableDeferred
87 import kotlinx.coroutines.Deferred
88 import kotlinx.coroutines.Dispatchers
89 import kotlinx.coroutines.runBlocking
90 import kotlinx.coroutines.withContext
91 import kotlinx.coroutines.withTimeout
92 import org.junit.After
93 import org.junit.Assume.assumeFalse
94 import org.junit.Assume.assumeTrue
95 import org.junit.Before
96 import org.junit.Rule
97 import org.junit.Test
98 import org.junit.runner.RunWith
99 
100 @LargeTest
101 @SdkSuppress(minSdkVersion = 26)
102 @RunWith(AndroidJUnit4::class)
103 class AdvancedSessionProcessorTest {
104     @get:Rule
105     val useCamera =
106         CameraUtil.grantCameraPermissionAndPreTestAndPostTest(
107             CameraUtil.PreTestCameraIdList(Camera2Config.defaultConfig())
108         )
109     private val context = ApplicationProvider.getApplicationContext<Context>()
110     private lateinit var cameraProvider: ProcessCameraProvider
111     private lateinit var fakeLifecycleOwner: FakeLifecycleOwner
112     private val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
113 
114     companion object {
115         const val TIMESTAMP = 1000L
116     }
117 
118     @Before
119     fun setUp() = runBlocking {
120         // Pixel lacks some Extensions-Interface methods / classes which cause the test failure.
121         assumeFalse(Build.MODEL.uppercase().startsWith("PIXEL"))
122         ExtensionVersion.injectInstance(null)
123         cameraProvider = ProcessCameraProvider.getInstance(context)[10, TimeUnit.SECONDS]
124         withContext(Dispatchers.Main) {
125             fakeLifecycleOwner = FakeLifecycleOwner()
126             fakeLifecycleOwner.startAndResume()
127         }
128     }
129 
130     @After
131     fun tearDown() = runBlocking {
132         if (::cameraProvider.isInitialized) {
133             withContext(Dispatchers.Main) { cameraProvider.shutdownAsync()[10, TimeUnit.SECONDS] }
134         }
135     }
136 
137     private fun getCameraSelectorWithSessionProcessor(
138         cameraSelector: CameraSelector,
139         sessionProcessor: SessionProcessor
140     ): CameraSelector {
141         val identifier = Identifier.create("idStr")
142         ExtendedCameraConfigProviderStore.addConfig(identifier) { _, _ ->
143             object : CameraConfig {
144                 override fun getConfig(): Config {
145                     return MutableOptionsBundle.create()
146                 }
147 
148                 override fun getCompatibilityId(): Identifier {
149                     return Identifier.create(0)
150                 }
151 
152                 override fun getSessionProcessor(
153                     valueIfMissing: SessionProcessor?
154                 ): SessionProcessor? {
155                     return sessionProcessor
156                 }
157 
158                 override fun getSessionProcessor(): SessionProcessor {
159                     return sessionProcessor
160                 }
161             }
162         }
163         val builder = CameraSelector.Builder.fromSelector(cameraSelector)
164         builder.addCameraFilter(
165             object : CameraFilter {
166                 override fun filter(cameraInfos: MutableList<CameraInfo>): MutableList<CameraInfo> {
167                     val newCameraInfos = mutableListOf<CameraInfo>()
168                     newCameraInfos.addAll(cameraInfos)
169                     return newCameraInfos
170                 }
171 
172                 override fun getIdentifier(): Identifier {
173                     return identifier
174                 }
175             }
176         )
177         return builder.build()
178     }
179 
180     @Test
181     fun useCasesCanWork_directlyUseOutputSurface_invokeOnCaptureCompleted() = runBlocking {
182         verifyUseCasesCanWork_directlyUseOutputSurface(invokeOnCaptureCompleted = true)
183     }
184 
185     @Test
186     fun useCasesCanWork_directlyUseOutputSurface_notInvokeOnCaptureCompleted() = runBlocking {
187         verifyUseCasesCanWork_directlyUseOutputSurface(invokeOnCaptureCompleted = false)
188     }
189 
190     private fun verifyUseCasesCanWork_directlyUseOutputSurface(invokeOnCaptureCompleted: Boolean) =
191         runBlocking {
192             val fakeSessionProcessImpl =
193                 FakeSessionProcessImpl(
194                     invokeOnCaptureCompleted = invokeOnCaptureCompleted,
195                     // Directly use output surface
196                     previewConfigBlock = { outputSurfaceImpl ->
197                         Camera2OutputConfigImplBuilder.newSurfaceConfig(outputSurfaceImpl.surface!!)
198                             .build()
199                     },
200                     // Directly use output surface
201                     captureConfigBlock = { outputSurfaceImpl ->
202                         Camera2OutputConfigImplBuilder.newSurfaceConfig(outputSurfaceImpl.surface!!)
203                             .build()
204                     },
205                     // Directly use output surface
206                     analysisConfigBlock = { outputSurfaceImpl ->
207                         Camera2OutputConfigImplBuilder.newSurfaceConfig(outputSurfaceImpl.surface!!)
208                             .build()
209                     }
210                 )
211 
212             val preview = Preview.Builder().build()
213             val imageCapture = ImageCapture.Builder().build()
214             val imageAnalysis = ImageAnalysis.Builder().build()
215             verifyUseCasesOutput(
216                 fakeSessionProcessImpl,
217                 preview,
218                 imageCapture,
219                 imageAnalysis,
220                 object : VendorExtender {
221                     @Suppress("UNCHECKED_CAST")
222                     override fun getSupportedCaptureResultKeys(): List<CaptureResult.Key<Any>> =
223                         if (invokeOnCaptureCompleted)
224                         // return a non-empty list to ensure onCaptureCompleted is invoked.
225                         listOf(CaptureResult.JPEG_QUALITY as CaptureResult.Key<Any>)
226                         else emptyList()
227                 }
228             )
229         }
230 
231     @Test
232     fun canInvokeStartTrigger() = runBlocking {
233         assumeTrue(ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_3))
234         val fakeSessionProcessImpl = FakeSessionProcessImpl()
235         val advancedSessionProcessor =
236             AdvancedSessionProcessor(
237                 fakeSessionProcessImpl,
238                 emptyList(),
239                 object : VendorExtender {},
240                 context
241             )
242 
243         val parametersMap: MutableMap<CaptureRequest.Key<*>, Any> =
244             mutableMapOf(
245                 CaptureRequest.CONTROL_AF_MODE to CaptureRequest.CONTROL_AF_MODE_AUTO,
246                 CaptureRequest.JPEG_QUALITY to 0
247             )
248 
249         val config =
250             RequestOptionConfig.Builder()
251                 .also {
252                     for (key in parametersMap.keys) {
253                         @Suppress("UNCHECKED_CAST") val anyKey = key as CaptureRequest.Key<Any>
254                         it.setCaptureRequestOption(anyKey, parametersMap[anyKey] as Any)
255                     }
256                 }
257                 .build()
258 
259         val tagBundle = MutableTagBundle.create()
260         val captureResultDeferred = CompletableDeferred<CameraCaptureResult>()
261         advancedSessionProcessor.startTrigger(
262             config,
263             tagBundle,
264             object : SessionProcessor.CaptureCallback {
265                 override fun onCaptureCompleted(
266                     timestamp: Long,
267                     captureSequenceId: Int,
268                     captureResult: CameraCaptureResult
269                 ) {
270                     captureResultDeferred.complete(captureResult)
271                 }
272             }
273         )
274 
275         fakeSessionProcessImpl.assertStartTriggerIsCalledWithParameters(parametersMap)
276         assertThat(captureResultDeferred.awaitWithTimeout(2000).tagBundle)
277             .isSameInstanceAs(tagBundle)
278     }
279 
280     @Test
281     fun getRealtimeLatencyEstimate_advancedSessionProcessorInvokesSessionProcessorImpl() =
282         runBlocking {
283             assumeTrue(ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_4))
284             ClientVersion.setCurrentVersion(ClientVersion("1.4.0"))
285 
286             val fakeSessionProcessImpl =
287                 object : SessionProcessorImpl by FakeSessionProcessImpl() {
288                     override fun getRealtimeCaptureLatency(): Pair<Long, Long> = Pair(1000L, 10L)
289                 }
290             val advancedSessionProcessor =
291                 AdvancedSessionProcessor(
292                     fakeSessionProcessImpl,
293                     emptyList(),
294                     object : VendorExtender {},
295                     context
296                 )
297 
298             val realtimeCaptureLatencyEstimate = advancedSessionProcessor.realtimeCaptureLatency
299 
300             assertThat(realtimeCaptureLatencyEstimate?.first).isEqualTo(1000L)
301             assertThat(realtimeCaptureLatencyEstimate?.second).isEqualTo(10L)
302         }
303 
304     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
305     @Test
306     fun isCurrentExtensionTypeAvailableReturnsCorrectFalseValue() = runBlocking {
307         assumeTrue(ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_4))
308         ClientVersion.setCurrentVersion(ClientVersion("1.4.0"))
309 
310         val advancedVendorExtender = AdvancedVendorExtender(FakeAdvancedExtenderImpl())
311 
312         val advancedSessionProcessor =
313             AdvancedSessionProcessor(
314                 FakeSessionProcessImpl(),
315                 emptyList(),
316                 advancedVendorExtender,
317                 context
318             )
319 
320         assertThat(advancedSessionProcessor.isCurrentExtensionModeAvailable).isFalse()
321     }
322 
323     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
324     @Suppress("UNCHECKED_CAST")
325     @Test
326     fun isCurrentExtensionTypeAvailableReturnsCorrectTrueValue() = runBlocking {
327         assumeTrue(ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_4))
328         ClientVersion.setCurrentVersion(ClientVersion("1.4.0"))
329 
330         val advancedVendorExtender =
331             AdvancedVendorExtender(
332                 FakeAdvancedExtenderImpl(
333                     testCaptureResultKeys =
334                         mutableListOf(
335                             CaptureResult.EXTENSION_CURRENT_TYPE as CaptureResult.Key<Any>
336                         )
337                 )
338             )
339 
340         val advancedSessionProcessor =
341             AdvancedSessionProcessor(
342                 FakeSessionProcessImpl(),
343                 emptyList(),
344                 advancedVendorExtender,
345                 context
346             )
347 
348         assertThat(advancedSessionProcessor.isCurrentExtensionModeAvailable).isTrue()
349     }
350 
351     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
352     @Test
353     fun isExtensionStrengthAvailableReturnsCorrectFalseValue() = runBlocking {
354         assumeTrue(ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_4))
355         ClientVersion.setCurrentVersion(ClientVersion("1.4.0"))
356 
357         val advancedVendorExtender = AdvancedVendorExtender(FakeAdvancedExtenderImpl())
358 
359         val advancedSessionProcessor =
360             AdvancedSessionProcessor(
361                 FakeSessionProcessImpl(),
362                 emptyList(),
363                 advancedVendorExtender,
364                 context
365             )
366 
367         assertThat(advancedSessionProcessor.isExtensionStrengthAvailable).isFalse()
368     }
369 
370     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
371     @Suppress("UNCHECKED_CAST")
372     @Test
373     fun isExtensionStrengthAvailableReturnsCorrectTrueValue() = runBlocking {
374         assumeTrue(ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_4))
375         ClientVersion.setCurrentVersion(ClientVersion("1.4.0"))
376 
377         val advancedVendorExtender =
378             AdvancedVendorExtender(
379                 FakeAdvancedExtenderImpl(
380                     testCaptureRequestKeys =
381                         mutableListOf(CaptureRequest.EXTENSION_STRENGTH as CaptureRequest.Key<Any>)
382                 )
383             )
384 
385         val advancedSessionProcessor =
386             AdvancedSessionProcessor(
387                 FakeSessionProcessImpl(),
388                 emptyList(),
389                 advancedVendorExtender,
390                 context
391             )
392 
393         assertThat(advancedSessionProcessor.isExtensionStrengthAvailable).isTrue()
394     }
395 
396     @Test
397     @SdkSuppress(minSdkVersion = 30)
398     fun getAvailableCharacteristicsKeyValuesIsReflected(): Unit = runBlocking {
399         assumeTrue(ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_5))
400         ClientVersion.setCurrentVersion(ClientVersion("1.5.0"))
401 
402         val availableStabilizationModes =
403             intArrayOf(CameraCharacteristics.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION)
404         val zoomRatioRange = Range(1f, 3f)
405 
406         val availableCharacteristicsKeyValues: List<Pair<CameraCharacteristics.Key<*>?, in Any>?> =
407             listOf(
408                 Pair(
409                     CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
410                     availableStabilizationModes
411                 ),
412                 Pair(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE, zoomRatioRange)
413             )
414 
415         val advancedVendorExtender =
416             AdvancedVendorExtender(
417                 FakeAdvancedExtenderImpl(
418                     testAvailableCharacteristics = availableCharacteristicsKeyValues
419                 )
420             )
421 
422         val advancedSessionProcessor =
423             AdvancedSessionProcessor(
424                 FakeSessionProcessImpl(),
425                 emptyList(),
426                 advancedVendorExtender,
427                 context
428             )
429 
430         assertThat(advancedSessionProcessor.availableCharacteristicsKeyValues)
431             .isEqualTo(availableCharacteristicsKeyValues)
432         assertThat(advancedSessionProcessor.extensionAvailableStabilizationModes)
433             .isEqualTo(availableStabilizationModes)
434         assertThat(advancedSessionProcessor.extensionZoomRange).isEqualTo(zoomRatioRange)
435     }
436 
437     private class FakeAdvancedExtenderImpl(
438         val testCaptureRequestKeys: MutableList<CaptureRequest.Key<Any>> = mutableListOf(),
439         val testCaptureResultKeys: MutableList<CaptureResult.Key<Any>> = mutableListOf(),
440         val testAvailableCharacteristics: List<Pair<CameraCharacteristics.Key<*>?, in Any>?> =
441             emptyList()
442     ) : AdvancedExtenderImpl {
443         override fun isExtensionAvailable(
444             cameraId: String,
445             characteristicsMap: MutableMap<String, CameraCharacteristics>
446         ): Boolean = true
447 
448         override fun init(
449             cameraId: String,
450             characteristicsMap: MutableMap<String, CameraCharacteristics>
451         ) {}
452 
453         override fun getEstimatedCaptureLatencyRange(
454             cameraId: String,
455             captureOutputSize: Size?,
456             imageFormat: Int
457         ): Range<Long>? = null
458 
459         override fun getSupportedPreviewOutputResolutions(
460             cameraId: String
461         ): MutableMap<Int, MutableList<Size>> = mutableMapOf()
462 
463         override fun getSupportedCaptureOutputResolutions(
464             cameraId: String
465         ): MutableMap<Int, MutableList<Size>> = mutableMapOf()
466 
467         override fun getSupportedPostviewResolutions(
468             captureSize: Size
469         ): MutableMap<Int, MutableList<Size>> = mutableMapOf()
470 
471         override fun getSupportedYuvAnalysisResolutions(cameraId: String): MutableList<Size>? = null
472 
473         override fun createSessionProcessor(): SessionProcessorImpl = FakeSessionProcessImpl()
474 
475         override fun getAvailableCaptureRequestKeys(): MutableList<CaptureRequest.Key<Any>> =
476             testCaptureRequestKeys
477 
478         override fun getAvailableCaptureResultKeys(): MutableList<CaptureResult.Key<Any>> =
479             testCaptureResultKeys
480 
481         override fun isCaptureProcessProgressAvailable(): Boolean = false
482 
483         override fun isPostviewAvailable(): Boolean = false
484 
485         override fun getAvailableCharacteristicsKeyValues() = testAvailableCharacteristics
486     }
487 
488     @Test
489     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
490     fun getCurrentExtensionType_advancedSessionProcessorMonitorSessionProcessorImplResults(): Unit =
491         runBlocking {
492             assumeTrue(ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_4))
493             ClientVersion.setCurrentVersion(ClientVersion("1.4.0"))
494 
495             val fakeSessionProcessImpl =
496                 object : SessionProcessorImpl by FakeSessionProcessImpl() {
497                     private var repeatingCallback: SessionProcessorImpl.CaptureCallback? = null
498 
499                     override fun startRepeating(
500                         callback: SessionProcessorImpl.CaptureCallback
501                     ): Int {
502                         repeatingCallback = callback
503                         return 0
504                     }
505 
506                     fun updateCurrentExtensionType(extensionType: Int) {
507                         repeatingCallback!!.onCaptureCompleted(
508                             0,
509                             0,
510                             mapOf(CaptureResult.EXTENSION_CURRENT_TYPE to extensionType)
511                         )
512                     }
513                 }
514             val advancedSessionProcessor =
515                 AdvancedSessionProcessor(
516                     fakeSessionProcessImpl,
517                     emptyList(),
518                     object : VendorExtender {
519                         override fun isCurrentExtensionModeAvailable(): Boolean {
520                             return true
521                         }
522                     },
523                     context,
524                     ExtensionMode.AUTO
525                 )
526 
527             // Starts repeating first to let fakeSessionProcessImpl obtain the
528             // AdvancedSessionProcessor's SessionProcessorImplCaptureCallbackAdapter instance
529             advancedSessionProcessor.startRepeating(
530                 TagBundle.emptyBundle(),
531                 object : SessionProcessor.CaptureCallback {}
532             )
533             val receivedTypeList = mutableListOf<Int>()
534             // Sets the count as 2 for receiving the initial extension type and the updated
535             // FACE_RETOUCH type
536             val countDownLatch = CountDownLatch(2)
537             withContext(Dispatchers.Main) {
538                 advancedSessionProcessor.currentExtensionMode.observeForever {
539                     receivedTypeList.add(it)
540                     countDownLatch.countDown()
541                 }
542             }
543             // Updates the current extension type capture result with Camera2 FACE_RETOUCH mode
544             fakeSessionProcessImpl.updateCurrentExtensionType(EXTENSION_FACE_RETOUCH)
545             // Verifies the new type value is updated to the type LiveData
546             assertThat(countDownLatch.await(200, TimeUnit.MILLISECONDS)).isTrue()
547             assertThat(receivedTypeList)
548                 .containsExactlyElementsIn(listOf(ExtensionMode.AUTO, ExtensionMode.FACE_RETOUCH))
549         }
550 
551     @Test
552     fun startCapture_tagBundleAndTimestampIsReturnedCorrectly_onCaptureCompletedInvoked() {
553         startCapture_tagBundleAndTimestampCorrectly(
554             fakeSessionProcessImpl =
555                 object : SessionProcessorImpl by FakeSessionProcessImpl() {
556                     override fun startCapture(callback: SessionProcessorImpl.CaptureCallback): Int {
557                         callback.onCaptureStarted(1, TIMESTAMP)
558                         callback.onCaptureCompleted(TIMESTAMP, 1, mapOf())
559                         callback.onCaptureSequenceCompleted(1)
560                         return 1
561                     }
562                 }
563         )
564     }
565 
566     @Test
567     fun startCapture_tagBundleAndTimestampIsReturnedCorrectly_onCaptureCompletedNotInvoked() {
568         startCapture_tagBundleAndTimestampCorrectly(
569             fakeSessionProcessImpl =
570                 object : SessionProcessorImpl by FakeSessionProcessImpl() {
571                     override fun startCapture(callback: SessionProcessorImpl.CaptureCallback): Int {
572                         callback.onCaptureStarted(1, TIMESTAMP)
573                         // do not invoke onCaptureCompleted
574                         callback.onCaptureSequenceCompleted(1)
575                         return 1
576                     }
577                 }
578         )
579     }
580 
581     private fun startCapture_tagBundleAndTimestampCorrectly(
582         fakeSessionProcessImpl: SessionProcessorImpl
583     ): Unit = runBlocking {
584         val advancedSessionProcessor =
585             AdvancedSessionProcessor(
586                 fakeSessionProcessImpl,
587                 emptyList(),
588                 object : VendorExtender {},
589                 context
590             )
591 
592         var tagBundle = MutableTagBundle.create()
593         val deferredCameraCaptureResult = CompletableDeferred<CameraCaptureResult>()
594 
595         advancedSessionProcessor.startCapture(
596             false,
597             tagBundle,
598             object : SessionProcessor.CaptureCallback {
599                 override fun onCaptureCompleted(
600                     timestamp: Long,
601                     captureSequenceId: Int,
602                     captureResult: CameraCaptureResult
603                 ) {
604                     deferredCameraCaptureResult.complete(captureResult)
605                 }
606             }
607         )
608         val captureResult = deferredCameraCaptureResult.awaitWithTimeout(2000)
609         assertThat(captureResult.tagBundle).isSameInstanceAs(tagBundle)
610         assertThat(captureResult.timestamp).isEqualTo(TIMESTAMP)
611     }
612 
613     @Test
614     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
615     fun setExtensionStrength_advancedSessionProcessorInvokesSessionProcessorImpl() = runBlocking {
616         assumeTrue(ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_4))
617         ClientVersion.setCurrentVersion(ClientVersion("1.4.0"))
618         val setParametersLatch = CountDownLatch(1)
619         val startRepeatingLatch = CountDownLatch(1)
620 
621         val fakeSessionProcessImpl =
622             object : SessionProcessorImpl by FakeSessionProcessImpl() {
623                 private val UNKNOWN_STRENGTH = -1
624                 private var strength = UNKNOWN_STRENGTH
625 
626                 override fun setParameters(parameters: MutableMap<CaptureRequest.Key<*>, Any>) {
627                     if (parameters.containsKey(CaptureRequest.EXTENSION_STRENGTH)) {
628                         strength = parameters[CaptureRequest.EXTENSION_STRENGTH] as Int
629                         setParametersLatch.countDown()
630                     }
631                 }
632 
633                 override fun startRepeating(callback: SessionProcessorImpl.CaptureCallback): Int {
634                     if (strength != UNKNOWN_STRENGTH) {
635                         // Updates the new strength result value to onCaptureCompleted
636                         callback.onCaptureCompleted(
637                             0,
638                             0,
639                             mapOf(CaptureResult.EXTENSION_STRENGTH to strength)
640                         )
641                     }
642                     startRepeatingLatch.countDown()
643                     return 0
644                 }
645             }
646         val advancedSessionProcessor =
647             AdvancedSessionProcessor(
648                 fakeSessionProcessImpl,
649                 emptyList(),
650                 object : VendorExtender {
651                     override fun isExtensionStrengthAvailable(): Boolean {
652                         return true
653                     }
654                 },
655                 context
656             )
657 
658         // Starts repeating first to verify that setExtensionStrength function will directly
659         // invoke the SessionProcessImpl#startRepeating function.
660         advancedSessionProcessor.startRepeating(
661             TagBundle.emptyBundle(),
662             object : SessionProcessor.CaptureCallback {}
663         )
664         val newExtensionStrength = 50
665         advancedSessionProcessor.setExtensionStrength(newExtensionStrength)
666         // Verifies that setExtensionStrength will invoke the SessionProcessImpl#setParameters
667         // function.
668         assertThat(setParametersLatch.await(200, TimeUnit.MILLISECONDS)).isTrue()
669         // Verifies that SessionProcessImpl#startRepeating function is invoked to apply the new
670         // strength value.
671         assertThat(startRepeatingLatch.await(200, TimeUnit.MILLISECONDS)).isTrue()
672         // Verifies the new strength value is updated to the strength LiveData
673         val expectedStrengthLatch = CountDownLatch(1)
674         withContext(Dispatchers.Main) {
675             advancedSessionProcessor.extensionStrength.observeForever {
676                 if (it == newExtensionStrength) {
677                     expectedStrengthLatch.countDown()
678                 }
679             }
680         }
681         assertThat(expectedStrengthLatch.await(200, TimeUnit.MILLISECONDS)).isTrue()
682     }
683 
684     @RequiresApi(28)
685     private suspend fun assumeAllowsSharedSurface() =
686         withContext(Dispatchers.Main) {
687             val imageReader = ImageReader.newInstance(640, 480, ImageFormat.YUV_420_888, 2)
688             val outputConfiguration = OutputConfiguration(imageReader.surface)
689             val maxSharedSurfaceCount = outputConfiguration.maxSharedSurfaceCount
690             imageReader.close()
691 
692             val camera = cameraProvider.bindToLifecycle(fakeLifecycleOwner, cameraSelector)
693             val cameraCharacteristics =
694                 (camera.cameraInfo as CameraInfoInternal).cameraCharacteristics
695                     as CameraCharacteristics
696 
697             val hardwareLevel =
698                 cameraCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL)
699             assumeTrue(
700                 maxSharedSurfaceCount > 1 &&
701                     hardwareLevel != CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
702             )
703         }
704 
705     // Surface sharing of YUV format is supported after API 28.
706     @SdkSuppress(minSdkVersion = 28)
707     @Test
708     fun useCasesCanWork_hasSharedSurfaceOutput() = runBlocking {
709         assumeAllowsSharedSurface()
710         var sharedConfigId = -1
711         val latchSharedSurfaceOutput = CountDownLatch(1)
712         val fakeSessionProcessImpl =
713             FakeSessionProcessImpl(
714                 // Directly use output surface
715                 previewConfigBlock = { outputSurfaceImpl ->
716                     Camera2OutputConfigImplBuilder.newSurfaceConfig(outputSurfaceImpl.surface!!)
717                         .build()
718                 },
719                 // Directly use output surface
720                 captureConfigBlock = { outputSurfaceImpl ->
721                     Camera2OutputConfigImplBuilder.newSurfaceConfig(outputSurfaceImpl.surface!!)
722                         .build()
723                 },
724                 // Directly use output surface with shared ImageReader surface.
725                 analysisConfigBlock = { outputSurfaceImpl ->
726                     val sharedConfig =
727                         Camera2OutputConfigImplBuilder.newImageReaderConfig(
728                                 outputSurfaceImpl.size,
729                                 outputSurfaceImpl.imageFormat,
730                                 2
731                             )
732                             .build()
733                     sharedConfigId = sharedConfig.id
734 
735                     Camera2OutputConfigImplBuilder.newSurfaceConfig(outputSurfaceImpl.surface!!)
736                         .addSurfaceSharingOutputConfig(sharedConfig)
737                         .build()
738                 },
739                 onCaptureSessionStarted = {
740                     it.setImageProcessor(sharedConfigId) { _, _, imageReference, _ ->
741                         imageReference.decrement()
742                         latchSharedSurfaceOutput.countDown()
743                     }
744                 }
745             )
746         val preview = Preview.Builder().build()
747         val imageCapture = ImageCapture.Builder().build()
748         val imageAnalysis = ImageAnalysis.Builder().build()
749 
750         verifyUseCasesOutput(fakeSessionProcessImpl, preview, imageCapture, imageAnalysis)
751         assertThat(latchSharedSurfaceOutput.await(3, TimeUnit.SECONDS)).isTrue()
752     }
753 
754     private fun getPhysicalCameraId(cameraSelector: CameraSelector): List<String> {
755         val cameraInfos = cameraSelector.filter(cameraProvider.availableCameraInfos)
756         if (cameraInfos.isEmpty()) {
757             return emptyList()
758         }
759         return CameraUtil.getPhysicalCameraIds((cameraInfos.get(0) as CameraInfoInternal).cameraId)
760     }
761 
762     // Test if physicalCameraId is set and returned in the image received in the image processor.
763     @SdkSuppress(minSdkVersion = 28) // physical camera id is supported in API28+
764     @Test
765     fun useCasesCanWork_setPhysicalCameraId() = runBlocking {
766         // OnePlus doesn't support physical camera well.
767         assumeFalse("oneplus".equals(Build.BRAND, ignoreCase = true))
768         assumeAllowsSharedSurface()
769         // Physical CameraId doesn't work on OnePlus
770         assumeFalse(Build.BRAND.uppercase().equals("ONEPLUS"))
771         val physicalCameraIdList = getPhysicalCameraId(cameraSelector)
772         assumeTrue(physicalCameraIdList.isNotEmpty())
773 
774         val physicalCameraId = physicalCameraIdList[0]
775         var analysisOutputSurface: Surface? = null
776         var sharedConfigId = -1
777         var intermediaConfigId = -1
778         val deferredImagePhysicalCameraId = CompletableDeferred<String?>()
779         val deferredSharedImagePhysicalCameraId = CompletableDeferred<String?>()
780 
781         val fakeSessionProcessImpl =
782             FakeSessionProcessImpl(
783                 // Directly use output surface
784                 previewConfigBlock = { outputSurfaceImpl ->
785                     Camera2OutputConfigImplBuilder.newSurfaceConfig(outputSurfaceImpl.surface!!)
786                         .setPhysicalCameraId(physicalCameraId)
787                         .build()
788                 },
789                 // Directly use output surface
790                 captureConfigBlock = {
791                     Camera2OutputConfigImplBuilder.newSurfaceConfig(it.surface!!).build()
792                 },
793                 // Has intermediate image reader to process YUV
794                 analysisConfigBlock = { outputSurfaceImpl ->
795                     analysisOutputSurface = outputSurfaceImpl.surface
796                     val sharedConfig =
797                         Camera2OutputConfigImplBuilder.newImageReaderConfig(
798                                 outputSurfaceImpl.size,
799                                 outputSurfaceImpl.imageFormat,
800                                 2
801                             )
802                             .setPhysicalCameraId(physicalCameraId)
803                             .build()
804                             .also { sharedConfigId = it.id }
805 
806                     Camera2OutputConfigImplBuilder.newImageReaderConfig(
807                             outputSurfaceImpl.size,
808                             ImageFormat.YUV_420_888,
809                             2
810                         )
811                         .setPhysicalCameraId(physicalCameraId)
812                         .addSurfaceSharingOutputConfig(sharedConfig)
813                         .build()
814                         .also { intermediaConfigId = it.id }
815                 },
816                 onCaptureSessionStarted = { requestProcessor ->
817                     val imageWriter = ImageWriter.newInstance(analysisOutputSurface!!, 2)
818                     requestProcessor.setImageProcessor(intermediaConfigId) {
819                         _,
820                         _,
821                         image,
822                         physicalCameraIdOfImage ->
823                         deferredImagePhysicalCameraId.complete(physicalCameraIdOfImage)
824                         val inputImage = imageWriter.dequeueInputImage()
825                         imageWriter.queueInputImage(inputImage)
826                         image.decrement()
827                     }
828                     requestProcessor.setImageProcessor(sharedConfigId) {
829                         _,
830                         _,
831                         imageReference,
832                         physicalCameraIdOfImage ->
833                         imageReference.decrement()
834                         deferredSharedImagePhysicalCameraId.complete(physicalCameraIdOfImage)
835                     }
836                 }
837             )
838 
839         val preview = Preview.Builder().build()
840         val imageCapture = ImageCapture.Builder().build()
841         val imageAnalysis = ImageAnalysis.Builder().build()
842         verifyUseCasesOutput(fakeSessionProcessImpl, preview, imageCapture, imageAnalysis)
843         assertThat(deferredImagePhysicalCameraId.awaitWithTimeout(2000)).isEqualTo(physicalCameraId)
844         assertThat(deferredSharedImagePhysicalCameraId.awaitWithTimeout(2000))
845             .isEqualTo(physicalCameraId)
846     }
847 
848     private fun createOutputSurface(width: Int, height: Int, format: Int): OutputSurface {
849         val captureImageReader = ImageReader.newInstance(width, height, format, 1)
850         return OutputSurface.create(captureImageReader.surface, Size(width, height), format)
851     }
852 
853     @Test
854     fun canSetSessionTypeFromOemImpl() {
855         assumeTrue(
856             ClientVersion.isMinimumCompatibleVersion(Version.VERSION_1_4) &&
857                 ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_4)
858         )
859         // 1. Arrange.
860         val sessionTypeToVerify = 4
861         val fakeSessionProcessImpl = FakeSessionProcessImpl()
862         fakeSessionProcessImpl.sessionType = sessionTypeToVerify
863         val advancedSessionProcessor =
864             AdvancedSessionProcessor(
865                 fakeSessionProcessImpl,
866                 emptyList(),
867                 object : VendorExtender {},
868                 context
869             )
870         val fakeCameraInfo = FakeCameraInfoInternal("0", context)
871         val previewOutputSurface = createOutputSurface(640, 480, ImageFormat.YUV_420_888)
872         val imageCaptureSurface = createOutputSurface(640, 480, ImageFormat.JPEG)
873 
874         // 2. Act.
875         val sessionConfig =
876             advancedSessionProcessor.initSession(
877                 fakeCameraInfo,
878                 OutputSurfaceConfiguration.create(
879                     previewOutputSurface,
880                     imageCaptureSurface,
881                     null,
882                     null
883                 )
884             )
885 
886         // 3. Assert.
887         assertThat(sessionConfig.sessionType).isEqualTo(sessionTypeToVerify)
888     }
889 
890     @Test
891     fun defaultSessionType() {
892         // 1. Arrange.
893         val fakeSessionProcessImpl = FakeSessionProcessImpl()
894         fakeSessionProcessImpl.sessionType = -1
895         val advancedSessionProcessor =
896             AdvancedSessionProcessor(
897                 fakeSessionProcessImpl,
898                 emptyList(),
899                 object : VendorExtender {},
900                 context
901             )
902         val fakeCameraInfo = FakeCameraInfoInternal("0", context)
903         val previewOutputSurface = createOutputSurface(640, 480, ImageFormat.YUV_420_888)
904         val imageCaptureSurface = createOutputSurface(640, 480, ImageFormat.JPEG)
905 
906         // 2. Act.
907         val sessionConfig =
908             advancedSessionProcessor.initSession(
909                 fakeCameraInfo,
910                 OutputSurfaceConfiguration.create(
911                     previewOutputSurface,
912                     imageCaptureSurface,
913                     null,
914                     null
915                 )
916             )
917 
918         // 3. Assert.
919         assertThat(sessionConfig.sessionType).isEqualTo(SessionConfiguration.SESSION_REGULAR)
920     }
921 
922     @Test
923     fun getSupportedPostviewSizeIsCorrect() {
924         // 1. Arrange
925         val postviewSizes =
926             mutableMapOf(ImageFormat.JPEG to listOf(Size(1920, 1080), Size(640, 480)))
927         val vendorExtender =
928             object : VendorExtender {
929                 override fun getSupportedPostviewResolutions(captureSize: Size) = postviewSizes
930             }
931         val advancedSessionProcessor =
932             AdvancedSessionProcessor(FakeSessionProcessImpl(), emptyList(), vendorExtender, context)
933 
934         // 2. Act and Assert
935         assertThat(
936                 advancedSessionProcessor
937                     .getSupportedPostviewSize(Size(1920, 1080))
938                     .get(ImageFormat.JPEG)
939             )
940             .containsExactly(Size(1920, 1080), Size(640, 480))
941     }
942 
943     /**
944      * Verify if the given use cases have expected output.
945      * 1) Preview frame is received
946      * 2) imageCapture gets a captured JPEG image
947      * 3) imageAnalysis gets a Image in Analyzer.
948      */
949     private suspend fun verifyUseCasesOutput(
950         fakeSessionProcessImpl: FakeSessionProcessImpl,
951         preview: Preview,
952         imageCapture: ImageCapture,
953         imageAnalysis: ImageAnalysis? = null,
954         vendorExtender: VendorExtender? = null
955     ) {
956         val advancedSessionProcessor =
957             AdvancedSessionProcessor(
958                 fakeSessionProcessImpl,
959                 emptyList(),
960                 vendorExtender ?: object : VendorExtender {},
961                 context
962             )
963         val latchPreviewFrame = CountDownLatch(1)
964         val latchAnalysis = CountDownLatch(1)
965         val deferCapturedImage = CompletableDeferred<ImageProxy>()
966 
967         withContext(Dispatchers.Main) {
968             preview.setSurfaceProvider(
969                 SurfaceTextureProvider.createSurfaceTextureProvider(
970                     object : SurfaceTextureCallback {
971                         override fun onSurfaceTextureReady(
972                             surfaceTexture: SurfaceTexture,
973                             resolution: Size
974                         ) {
975                             surfaceTexture.setOnFrameAvailableListener {
976                                 latchPreviewFrame.countDown()
977                             }
978                         }
979 
980                         override fun onSafeToRelease(surfaceTexture: SurfaceTexture) {
981                             surfaceTexture.release()
982                         }
983                     }
984                 )
985             )
986             imageAnalysis?.setAnalyzer(CameraXExecutors.mainThreadExecutor()) {
987                 it.close()
988                 latchAnalysis.countDown()
989             }
990             val cameraSelector =
991                 getCameraSelectorWithSessionProcessor(cameraSelector, advancedSessionProcessor)
992 
993             val useCaseGroupBuilder = UseCaseGroup.Builder()
994             useCaseGroupBuilder.addUseCase(preview)
995             useCaseGroupBuilder.addUseCase(imageCapture)
996             imageAnalysis?.let { useCaseGroupBuilder.addUseCase(it) }
997 
998             cameraProvider.bindToLifecycle(
999                 fakeLifecycleOwner,
1000                 cameraSelector,
1001                 useCaseGroupBuilder.build()
1002             )
1003         }
1004 
1005         imageCapture.takePicture(
1006             CameraXExecutors.mainThreadExecutor(),
1007             object : OnImageCapturedCallback() {
1008                 override fun onCaptureSuccess(image: ImageProxy) {
1009                     deferCapturedImage.complete(image)
1010                 }
1011             }
1012         )
1013 
1014         assertThat(latchPreviewFrame.await(3, TimeUnit.SECONDS)).isTrue()
1015         assertThat(deferCapturedImage.awaitWithTimeout(5000).image!!.format)
1016             .isEqualTo(ImageFormat.JPEG)
1017         imageAnalysis?.let { assertThat(latchAnalysis.await(3, TimeUnit.SECONDS)).isTrue() }
1018     }
1019 }
1020 
awaitWithTimeoutnull1021 suspend fun <T> Deferred<T>.awaitWithTimeout(timeMillis: Long): T {
1022     return withTimeout(timeMillis) { await() }
1023 }
1024 
1025 /**
1026  * A fake [SessionProcessorImpl] where preview/imageCapture/imageAnalysis camera2OutputConfigImpl
1027  * can be customized via input blocks. onCaptureSessionStarted block can also be provided in order
1028  * to allow tests to access the [RequestProcessorImpl] to receive the Image for
1029  * [ImageReaderOutputConfigImpl].
1030  */
1031 class FakeSessionProcessImpl(
outputSurfaceImplnull1032     var previewConfigBlock: (OutputSurfaceImpl) -> Camera2OutputConfigImpl = { outputSurfaceImpl ->
1033         Camera2OutputConfigImplBuilder.newSurfaceConfig(outputSurfaceImpl.surface!!).build()
1034     },
outputSurfaceImplnull1035     var captureConfigBlock: (OutputSurfaceImpl) -> Camera2OutputConfigImpl = { outputSurfaceImpl ->
1036         Camera2OutputConfigImplBuilder.newSurfaceConfig(outputSurfaceImpl.surface!!).build()
1037     },
1038     var analysisConfigBlock: ((OutputSurfaceImpl) -> Camera2OutputConfigImpl)? = null,
1039     var onCaptureSessionStarted: ((RequestProcessorImpl) -> Unit)? = null,
1040     val invokeOnCaptureCompleted: Boolean = true
1041 ) : SessionProcessorImpl {
1042     private var requestProcessor: RequestProcessorImpl? = null
1043     private var nextSequenceId = 0
1044     private var nextTimestamp = 0L
1045     private lateinit var previewOutputConfig: Camera2OutputConfigImpl
1046     private lateinit var captureOutputConfig: Camera2OutputConfigImpl
1047     private var analysisOutputConfig: Camera2OutputConfigImpl? = null
1048     private var sharedOutputConfigList = arrayListOf<Camera2OutputConfigImpl>()
1049     private var startTriggerParametersDeferred =
1050         CompletableDeferred<MutableMap<CaptureRequest.Key<*>, Any>>()
1051 
1052     var sessionType: Int = -1
1053 
initSessionnull1054     override fun initSession(
1055         cameraId: String,
1056         cameraCharacteristicsMap: MutableMap<String, CameraCharacteristics>,
1057         context: Context,
1058         previewSurfaceConfig: OutputSurfaceImpl,
1059         captureSurfaceConfig: OutputSurfaceImpl,
1060         analysisSurfaceConfig: OutputSurfaceImpl?
1061     ): Camera2SessionConfigImpl {
1062         captureOutputConfig = captureConfigBlock.invoke(captureSurfaceConfig)
1063         previewOutputConfig = previewConfigBlock.invoke(previewSurfaceConfig)
1064         analysisSurfaceConfig?.let { analysisOutputConfig = analysisConfigBlock?.invoke(it) }
1065 
1066         captureOutputConfig.surfaceSharingOutputConfigs?.let { sharedOutputConfigList.addAll(it) }
1067         previewOutputConfig.surfaceSharingOutputConfigs?.let { sharedOutputConfigList.addAll(it) }
1068         analysisOutputConfig?.surfaceSharingOutputConfigs?.let { sharedOutputConfigList.addAll(it) }
1069 
1070         val sessionBuilder =
1071             Camera2SessionConfigImplBuilder().apply {
1072                 addOutputConfig(previewOutputConfig)
1073                 addOutputConfig(captureOutputConfig)
1074                 analysisOutputConfig?.let { addOutputConfig(it) }
1075             }
1076 
1077         if (ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_4) && sessionType != -1) {
1078             sessionBuilder.setSessionType(sessionType)
1079         }
1080         return sessionBuilder.build()
1081     }
1082 
initSessionnull1083     override fun initSession(
1084         cameraId: String,
1085         cameraCharacteristicsMap: MutableMap<String, CameraCharacteristics>,
1086         context: Context,
1087         surfaceConfigs: OutputSurfaceConfigurationImpl
1088     ): Camera2SessionConfigImpl {
1089         return initSession(
1090             cameraId,
1091             cameraCharacteristicsMap,
1092             context,
1093             surfaceConfigs.previewOutputSurface,
1094             surfaceConfigs.imageCaptureOutputSurface,
1095             surfaceConfigs.imageAnalysisOutputSurface
1096         )
1097     }
1098 
deInitSessionnull1099     override fun deInitSession() {}
1100 
setParametersnull1101     override fun setParameters(parameters: MutableMap<CaptureRequest.Key<*>, Any>) {}
1102 
startTriggernull1103     override fun startTrigger(
1104         triggers: MutableMap<CaptureRequest.Key<*>, Any>,
1105         callback: SessionProcessorImpl.CaptureCallback
1106     ): Int {
1107         startTriggerParametersDeferred.complete(triggers)
1108         val timestamp = nextTimestamp++
1109         val sequenceId = nextSequenceId++
1110         callback.onCaptureStarted(sequenceId, timestamp)
1111         if (invokeOnCaptureCompleted) {
1112             callback.onCaptureCompleted(timestamp, sequenceId, emptyMap())
1113         }
1114         callback.onCaptureSequenceCompleted(sequenceId)
1115         return sequenceId
1116     }
1117 
assertStartTriggerIsCalledWithParametersnull1118     suspend fun assertStartTriggerIsCalledWithParameters(
1119         parameters: MutableMap<CaptureRequest.Key<*>, Any>
1120     ) {
1121         assertThat(startTriggerParametersDeferred.awaitWithTimeout(1000)).isEqualTo(parameters)
1122     }
1123 
onCaptureSessionStartnull1124     override fun onCaptureSessionStart(requestProcessor: RequestProcessorImpl) {
1125         this.requestProcessor = requestProcessor
1126         onCaptureSessionStarted?.invoke(requestProcessor)
1127     }
1128 
onCaptureSessionEndnull1129     override fun onCaptureSessionEnd() {}
1130 
startRepeatingnull1131     override fun startRepeating(callback: SessionProcessorImpl.CaptureCallback): Int {
1132         val idList = ArrayList<Int>()
1133         idList.add(previewOutputConfig.id)
1134         analysisOutputConfig?.let { idList.add(it.id) }
1135         for (sharedConfig in sharedOutputConfigList) {
1136             idList.add(sharedConfig.id)
1137         }
1138 
1139         val currentSequenceId = nextSequenceId++
1140         val request = RequestProcessorRequest(idList, mapOf(), CameraDevice.TEMPLATE_PREVIEW)
1141         requestProcessor!!.setRepeating(
1142             request,
1143             object : RequestProcessorImpl.Callback {
1144                 override fun onCaptureStarted(
1145                     request: RequestProcessorImpl.Request,
1146                     frameNumber: Long,
1147                     timestamp: Long
1148                 ) {
1149                     callback.onCaptureStarted(currentSequenceId, timestamp)
1150                 }
1151 
1152                 override fun onCaptureProgressed(
1153                     request: RequestProcessorImpl.Request,
1154                     partialResult: CaptureResult
1155                 ) {}
1156 
1157                 override fun onCaptureCompleted(
1158                     request: RequestProcessorImpl.Request,
1159                     totalCaptureResult: TotalCaptureResult
1160                 ) {
1161                     callback.onCaptureProcessStarted(currentSequenceId)
1162                     if (invokeOnCaptureCompleted) {
1163                         callback.onCaptureCompleted(
1164                             totalCaptureResult.get(CaptureResult.SENSOR_TIMESTAMP)!!,
1165                             currentSequenceId,
1166                             mapOf()
1167                         )
1168                     }
1169                     callback.onCaptureSequenceCompleted(currentSequenceId)
1170                 }
1171 
1172                 override fun onCaptureFailed(
1173                     request: RequestProcessorImpl.Request,
1174                     captureFailure: CaptureFailure
1175                 ) {
1176                     callback.onCaptureFailed(currentSequenceId)
1177                     callback.onCaptureSequenceAborted(currentSequenceId)
1178                 }
1179 
1180                 override fun onCaptureBufferLost(
1181                     request: RequestProcessorImpl.Request,
1182                     frameNumber: Long,
1183                     outputStreamId: Int
1184                 ) {}
1185 
1186                 override fun onCaptureSequenceCompleted(sequenceId: Int, frameNumber: Long) {}
1187 
1188                 override fun onCaptureSequenceAborted(sequenceId: Int) {}
1189             }
1190         )
1191         return currentSequenceId
1192     }
1193 
stopRepeatingnull1194     override fun stopRepeating() {
1195         requestProcessor?.stopRepeating()
1196     }
1197 
startCapturenull1198     override fun startCapture(callback: SessionProcessorImpl.CaptureCallback): Int {
1199         val idList = ArrayList<Int>()
1200         idList.add(captureOutputConfig.id)
1201 
1202         val currentSequenceId = nextSequenceId++
1203         val request = RequestProcessorRequest(idList, mapOf(), CameraDevice.TEMPLATE_STILL_CAPTURE)
1204         requestProcessor?.submit(
1205             request,
1206             object : RequestProcessorImpl.Callback {
1207                 override fun onCaptureStarted(
1208                     request: RequestProcessorImpl.Request,
1209                     frameNumber: Long,
1210                     timestamp: Long
1211                 ) {
1212                     callback.onCaptureStarted(currentSequenceId, timestamp)
1213                 }
1214 
1215                 override fun onCaptureProgressed(
1216                     request: RequestProcessorImpl.Request,
1217                     partialResult: CaptureResult
1218                 ) {}
1219 
1220                 override fun onCaptureCompleted(
1221                     request: RequestProcessorImpl.Request,
1222                     totalCaptureResult: TotalCaptureResult
1223                 ) {
1224                     if (invokeOnCaptureCompleted) {
1225                         callback.onCaptureCompleted(
1226                             totalCaptureResult.get(CaptureResult.SENSOR_TIMESTAMP)!!,
1227                             currentSequenceId,
1228                             mapOf()
1229                         )
1230                     }
1231                 }
1232 
1233                 override fun onCaptureFailed(
1234                     request: RequestProcessorImpl.Request,
1235                     captureFailure: CaptureFailure
1236                 ) {
1237                     callback.onCaptureFailed(currentSequenceId)
1238                 }
1239 
1240                 override fun onCaptureBufferLost(
1241                     request: RequestProcessorImpl.Request,
1242                     frameNumber: Long,
1243                     outputStreamId: Int
1244                 ) {}
1245 
1246                 override fun onCaptureSequenceCompleted(sequenceId: Int, frameNumber: Long) {
1247                     callback.onCaptureSequenceCompleted(currentSequenceId)
1248                 }
1249 
1250                 override fun onCaptureSequenceAborted(sequenceId: Int) {
1251                     callback.onCaptureSequenceAborted(currentSequenceId)
1252                 }
1253             }
1254         )
1255         return currentSequenceId
1256     }
1257 
startCaptureWithPostviewnull1258     override fun startCaptureWithPostview(callback: SessionProcessorImpl.CaptureCallback): Int {
1259         return startCapture(callback)
1260     }
1261 
getRealtimeCaptureLatencynull1262     override fun getRealtimeCaptureLatency(): Pair<Long, Long>? {
1263         return null
1264     }
1265 
abortCapturenull1266     override fun abortCapture(captureSequenceId: Int) {
1267         requestProcessor?.abortCaptures()
1268     }
1269 }
1270 
1271 class RequestProcessorRequest(
1272     private val targetOutputConfigIds: List<Int>,
1273     private val parameters: Map<CaptureRequest.Key<*>, Any>,
1274     private val templateId: Int
1275 ) : RequestProcessorImpl.Request {
getTargetOutputConfigIdsnull1276     override fun getTargetOutputConfigIds(): List<Int> {
1277         return targetOutputConfigIds
1278     }
1279 
getParametersnull1280     override fun getParameters(): Map<CaptureRequest.Key<*>, Any> {
1281         return parameters
1282     }
1283 
getTemplateIdnull1284     override fun getTemplateId(): Int {
1285         return templateId
1286     }
1287 }
1288