1 /* 2 * Copyright (C) 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 com.android.deviceaswebcam 18 19 import android.content.Context 20 import android.graphics.SurfaceTexture 21 import android.util.Size 22 import com.android.deviceaswebcam.CameraController.RotationUpdateListener 23 import java.util.function.Consumer 24 25 class WebcamControllerImpl(context: Context) : WebcamController() { 26 private val mLock = Object() 27 28 private val mCameraController = CameraController(context, /* webcamController= */ this) 29 private var mDestroyActivityCallback: Runnable? = null 30 setStreamConfignull31 override fun setStreamConfig(size: Size, frameRate: Int) { 32 synchronized(mLock) { 33 mCameraController.setWebcamStreamConfig(size.width, size.height, frameRate) 34 } 35 } 36 startStreamnull37 override fun startStream() { 38 synchronized(mLock) { mCameraController.startWebcamStreaming() } 39 } 40 stopStreamnull41 override fun stopStream() { 42 synchronized(mLock) { mCameraController.stopWebcamStreaming() } 43 } 44 onImageReturnednull45 override fun onImageReturned(token: Long) { 46 synchronized(mLock) { mCameraController.returnImage(token) } 47 } 48 onDestroynull49 override fun onDestroy() { 50 synchronized(mLock) { mDestroyActivityCallback?.run() } 51 } 52 53 /** 54 * Method to set a preview surface texture that camera will stream to. Should be of the size 55 * returned by [.getSuitablePreviewSize]. 56 * 57 * @param surfaceTexture surfaceTexture to stream preview frames to 58 * @param previewSize the preview size 59 * @param previewSizeChangeListener a listener to monitor the preview size change events. 60 */ setPreviewSurfaceTexturenull61 fun setPreviewSurfaceTexture( 62 surfaceTexture: SurfaceTexture, 63 previewSize: Size, 64 previewSizeChangeListener: Consumer<Size>? 65 ) { 66 synchronized(mLock) { 67 mCameraController.startPreviewStreaming( 68 surfaceTexture, 69 previewSize, 70 previewSizeChangeListener 71 ) 72 } 73 } 74 75 /** Returns the available [CameraId] list. */ getAvailableCameraIdsnull76 fun getAvailableCameraIds(): List<CameraId> { 77 synchronized(mLock) { 78 return mCameraController.availableCameraIds 79 } 80 } 81 82 /** Returns current rotation degrees value. */ getCurrentRotationnull83 fun getCurrentRotation(): Int { 84 synchronized(mLock) { 85 return mCameraController.currentRotation 86 } 87 } 88 89 /** Sets a [CameraController.RotationUpdateListener] to monitor the device rotation changes. */ setRotationUpdateListenernull90 fun setRotationUpdateListener(listener: RotationUpdateListener?) { 91 synchronized(mLock) { mCameraController.setRotationUpdateListener(listener) } 92 } 93 94 /** Method to remove any preview SurfaceTexture set by [.setPreviewSurfaceTexture]. */ removePreviewSurfaceTexturenull95 fun removePreviewSurfaceTexture() { 96 synchronized(mLock) { mCameraController.stopPreviewStreaming() } 97 } 98 99 /** Sets the new zoom ratio setting to the working camera. */ setZoomRationull100 fun setZoomRatio(zoomRatio: Float) { 101 synchronized(mLock) { mCameraController.zoomRatio = zoomRatio } 102 } 103 104 /** Returns the [CameraInfo] of the working camera. */ getCameraInfonull105 fun getCameraInfo(): CameraInfo? { 106 synchronized(mLock) { 107 return mCameraController.cameraInfo 108 } 109 } 110 111 /** 112 * Retrieves current tap-to-focus points. 113 * 114 * @return the normalized points or `null` if it is auto-focus mode currently. 115 */ getTapToFocusPointsnull116 fun getTapToFocusPoints(): FloatArray? { 117 synchronized(mLock) { 118 return mCameraController.tapToFocusPoints 119 } 120 } 121 122 /** Returns true if high quality mode is enabled, false otherwise */ isHighQualityModeEnablednull123 fun isHighQualityModeEnabled(): Boolean { 124 synchronized(mLock) { 125 return mCameraController.isHighQualityModeEnabled 126 } 127 } 128 129 /** 130 * Enables/Disables high quality mode. See [CameraController.setHighQualityModeEnabled] for more 131 * info. 132 */ setHighQualityModeEnablednull133 fun setHighQualityModeEnabled(enabled: Boolean, callback: Runnable) { 134 synchronized(mLock) { mCameraController.setHighQualityModeEnabled(enabled, callback) } 135 } 136 137 /** 138 * Returns the best suitable output size for preview. 139 * 140 * If the webcam stream doesn't exist, find the largest 16:9 supported output size which is not 141 * larger than 1080p. If the webcam stream exists, find the largest supported output size which 142 * matches the aspect ratio of the webcam stream size and is not larger than the webcam stream 143 * size. 144 */ getSuitablePreviewSizenull145 fun getSuitablePreviewSize(): Size? { 146 synchronized(mLock) { 147 return mCameraController.suitablePreviewSize 148 } 149 } 150 151 /** Returns current zoom ratio setting. */ getZoomRationull152 fun getZoomRatio(): Float { 153 synchronized(mLock) { 154 return mCameraController.zoomRatio 155 } 156 } 157 158 /** 159 * Method to setOnDestroyedCallback. This callback will be called when immediately before the 160 * foreground service is destroyed. Intended to give and bound context a change to clean up 161 * before the Service is destroyed. `setOnDestroyedCallback(null)` must be called to unset the 162 * callback when a bound context finishes to prevent Context leak. 163 * 164 * This callback must not call `setOnDestroyedCallback` from within the callback. 165 * 166 * @param callback callback to be called when the service is destroyed. `null` unsets the 167 * callback 168 */ setOnDestroyedCallbacknull169 fun setOnDestroyedCallback(callback: Runnable?) { 170 synchronized(mLock) { mDestroyActivityCallback = callback } 171 } 172 173 /** Returns the [CameraInfo] for the specified camera id. */ getOrCreateCameraInfonull174 fun getOrCreateCameraInfo(cameraId: CameraId): CameraInfo? { 175 synchronized(mLock) { 176 return mCameraController.getOrCreateCameraInfo(cameraId) 177 } 178 } 179 180 /** Toggles camera between the back and front cameras. */ toggleCameranull181 fun toggleCamera() { 182 synchronized(mLock) { mCameraController.toggleCamera() } 183 } 184 185 /** Switches current working camera to specific one. */ switchCameranull186 fun switchCamera(cameraId: CameraId) { 187 synchronized(mLock) { mCameraController.switchCamera(cameraId) } 188 } 189 190 /** Resets to the auto-focus mode. */ resetToAutoFocusnull191 fun resetToAutoFocus() { 192 synchronized(mLock) { mCameraController.resetToAutoFocus() } 193 } 194 195 /** 196 * Trigger tap-to-focus operation for the specified normalized points mapping to the FOV. 197 * 198 * The specified normalized points will be used to calculate the corresponding metering 199 * rectangles that will be applied for AF, AE and AWB. 200 */ tapToFocusnull201 fun tapToFocus(normalizedPoint: FloatArray?) { 202 synchronized(mLock) { mCameraController.tapToFocus(normalizedPoint) } 203 } 204 205 companion object { 206 private const val TAG = "WebcamControllerImpl" 207 } 208 } 209