1 /* 2 * Copyright 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.camera.camera2.pipe.testing 18 19 import android.view.Surface 20 import androidx.annotation.GuardedBy 21 import androidx.camera.camera2.pipe.CameraStream 22 import androidx.camera.camera2.pipe.ImageSourceConfig 23 import androidx.camera.camera2.pipe.StreamId 24 import androidx.camera.camera2.pipe.media.ImageSource 25 import androidx.camera.camera2.pipe.media.ImageSources 26 27 /** 28 * Utility class for creating, tracking, and simulating [FakeImageSource]s. [FakeImageSource](s) can 29 * be retrieved based on [Surface] or by [StreamId], and supports both single and 30 * MultiResolutionImageReader-like implementations. 31 */ 32 public class FakeImageSources(private val fakeImageReaders: FakeImageReaders) : ImageSources { 33 private val lock = Any() 34 35 @GuardedBy("lock") private val fakeImageSources = mutableListOf<FakeImageSource>() 36 getnull37 public operator fun get(surface: Surface): FakeImageSource? { 38 return synchronized(lock) { fakeImageSources.find { it.surface == surface } } 39 } 40 getnull41 public operator fun get(streamId: StreamId): FakeImageSource? { 42 return synchronized(lock) { fakeImageSources.find { it.streamId == streamId } } 43 } 44 createImageSourcenull45 override fun createImageSource( 46 cameraStream: CameraStream, 47 imageSourceConfig: ImageSourceConfig 48 ): ImageSource { 49 check(this[cameraStream.id] == null) { 50 "Cannot create multiple ImageSource(s) from the same $cameraStream!" 51 } 52 val fakeImageSource = 53 FakeImageSource.create( 54 cameraStream.outputs.first().format, 55 cameraStream.id, 56 cameraStream.outputs.associate { it.id to it.size }, 57 imageSourceConfig.capacity, 58 fakeImageReaders 59 ) 60 synchronized(lock) { fakeImageSources.add(fakeImageSource) } 61 return fakeImageSource 62 } 63 64 /** [check] that all [FakeImageSource]s are closed. */ checkImageSourcesClosednull65 public fun checkImageSourcesClosed(): Unit = 66 synchronized(lock) { 67 for (fakeImageSource in fakeImageSources) { 68 check(fakeImageSource.isClosed) { "Failed to close ImageSource!: $fakeImageSource" } 69 } 70 } 71 72 /** [check] that all images from all [FakeImageReader]s are closed. */ checkImagesClosednull73 public fun checkImagesClosed(): Unit = 74 synchronized(lock) { 75 for ((i, fakeImageSource) in fakeImageSources.withIndex()) { 76 for ((j, fakeImage) in fakeImageSource.images.withIndex()) { 77 check(fakeImage.isClosed) { 78 "Failed to close $fakeImage ($j / " + 79 "${fakeImageSource.images.size}) from $fakeImageSource " + 80 "($i / ${fakeImageSources.size})" 81 } 82 } 83 } 84 } 85 } 86