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