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