Committed by
GitHub
Allow registration of external cameras implementations (#345)
* Allow registration of external cameras implementations * Update livekit-android-sdk/src/main/java/io/livekit/android/room/track/video/CameraCapturerUtils.kt * Update livekit-android-sdk/src/main/java/io/livekit/android/room/track/video/CameraCapturerUtils.kt * Remove internal and suppress tags to make camera capturers public * Update CameraEventsDispatchHandler.kt * spotless --------- Co-authored-by: davidliu <davidliu@deviange.net>
正在显示
3 个修改的文件
包含
107 行增加
和
53 行删除
| @@ -36,18 +36,45 @@ import livekit.org.webrtc.VideoCapturer | @@ -36,18 +36,45 @@ import livekit.org.webrtc.VideoCapturer | ||
| 36 | */ | 36 | */ |
| 37 | object CameraCapturerUtils { | 37 | object CameraCapturerUtils { |
| 38 | 38 | ||
| 39 | + private val cameraProviders = mutableListOf<CameraProvider>().apply { | ||
| 40 | + add(createCamera1Provider()) | ||
| 41 | + add(createCamera2Provider()) | ||
| 42 | + } | ||
| 43 | + | ||
| 39 | /** | 44 | /** |
| 40 | - * Create a CameraEnumerator based on platform capabilities. | ||
| 41 | - * | ||
| 42 | - * If available, creates an enumerator that uses Camera2. If not, | ||
| 43 | - * a Camera1 enumerator is created. | 45 | + * Register external camera provider |
| 46 | + */ | ||
| 47 | + fun registerCameraProvider(cameraProvider: CameraProvider) { | ||
| 48 | + LKLog.d { "Registering camera provider: Camera version:${cameraProvider.cameraVersion}" } | ||
| 49 | + cameraProviders.add(cameraProvider) | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + /** | ||
| 53 | + * Unregister external camera provider | ||
| 54 | + */ | ||
| 55 | + fun unregisterCameraProvider(cameraProvider: CameraProvider) { | ||
| 56 | + LKLog.d { "Removing camera provider: Camera version:${cameraProvider.cameraVersion}" } | ||
| 57 | + cameraProviders.remove(cameraProvider) | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + /** | ||
| 61 | + * Obtain a CameraEnumerator based on platform capabilities. | ||
| 44 | */ | 62 | */ |
| 45 | fun createCameraEnumerator(context: Context): CameraEnumerator { | 63 | fun createCameraEnumerator(context: Context): CameraEnumerator { |
| 46 | - return if (Camera2Enumerator.isSupported(context)) { | ||
| 47 | - Camera2Enumerator(context) | ||
| 48 | - } else { | ||
| 49 | - Camera1Enumerator(true) | ||
| 50 | - } | 64 | + return getCameraProvider(context).provideEnumerator(context) |
| 65 | + } | ||
| 66 | + | ||
| 67 | + /** | ||
| 68 | + * Create a CameraProvider based on platform capabilities. | ||
| 69 | + * | ||
| 70 | + * Picks CameraProvider of highest available version that is supported on device | ||
| 71 | + */ | ||
| 72 | + private fun getCameraProvider(context: Context): CameraProvider { | ||
| 73 | + return cameraProviders | ||
| 74 | + .sortedByDescending { it.cameraVersion } | ||
| 75 | + .first { | ||
| 76 | + it.isSupported(context) | ||
| 77 | + } | ||
| 51 | } | 78 | } |
| 52 | 79 | ||
| 53 | /** | 80 | /** |
| @@ -57,8 +84,7 @@ object CameraCapturerUtils { | @@ -57,8 +84,7 @@ object CameraCapturerUtils { | ||
| 57 | context: Context, | 84 | context: Context, |
| 58 | options: LocalVideoTrackOptions, | 85 | options: LocalVideoTrackOptions, |
| 59 | ): Pair<VideoCapturer, LocalVideoTrackOptions>? { | 86 | ): Pair<VideoCapturer, LocalVideoTrackOptions>? { |
| 60 | - val cameraEnumerator = createCameraEnumerator(context) | ||
| 61 | - val pair = createCameraCapturer(context, cameraEnumerator, options) | 87 | + val pair = createCameraCapturer(context, getCameraProvider(context), options) |
| 62 | 88 | ||
| 63 | if (pair == null) { | 89 | if (pair == null) { |
| 64 | LKLog.d { "Failed to open camera" } | 90 | LKLog.d { "Failed to open camera" } |
| @@ -69,52 +95,82 @@ object CameraCapturerUtils { | @@ -69,52 +95,82 @@ object CameraCapturerUtils { | ||
| 69 | 95 | ||
| 70 | private fun createCameraCapturer( | 96 | private fun createCameraCapturer( |
| 71 | context: Context, | 97 | context: Context, |
| 72 | - enumerator: CameraEnumerator, | 98 | + provider: CameraProvider, |
| 73 | options: LocalVideoTrackOptions, | 99 | options: LocalVideoTrackOptions, |
| 74 | ): Pair<VideoCapturer, LocalVideoTrackOptions>? { | 100 | ): Pair<VideoCapturer, LocalVideoTrackOptions>? { |
| 75 | val cameraEventsDispatchHandler = CameraEventsDispatchHandler() | 101 | val cameraEventsDispatchHandler = CameraEventsDispatchHandler() |
| 76 | - val targetDeviceName = enumerator.findCamera(options.deviceId, options.position) ?: return null | ||
| 77 | - val targetVideoCapturer = enumerator.createCapturer(targetDeviceName, cameraEventsDispatchHandler) | 102 | + val cameraEnumerator = provider.provideEnumerator(context) |
| 103 | + val targetDeviceName = cameraEnumerator.findCamera(options.deviceId, options.position) ?: return null | ||
| 104 | + val targetVideoCapturer = provider.provideCapturer(context, options, cameraEventsDispatchHandler) | ||
| 78 | 105 | ||
| 79 | // back fill any missing information | 106 | // back fill any missing information |
| 80 | val newOptions = options.copy( | 107 | val newOptions = options.copy( |
| 81 | deviceId = targetDeviceName, | 108 | deviceId = targetDeviceName, |
| 82 | - position = enumerator.getCameraPosition(targetDeviceName), | 109 | + position = cameraEnumerator.getCameraPosition(targetDeviceName), |
| 83 | ) | 110 | ) |
| 84 | - if (targetVideoCapturer is Camera1Capturer) { | ||
| 85 | - // Cache supported capture formats ahead of time to avoid future camera locks. | ||
| 86 | - Camera1Helper.getSupportedFormats(Camera1Helper.getCameraId(newOptions.deviceId)) | ||
| 87 | - return Pair( | ||
| 88 | - Camera1CapturerWithSize( | ||
| 89 | - targetVideoCapturer, | ||
| 90 | - targetDeviceName, | ||
| 91 | - cameraEventsDispatchHandler, | ||
| 92 | - ), | ||
| 93 | - newOptions, | ||
| 94 | - ) | 111 | + |
| 112 | + if (targetVideoCapturer !is VideoCapturerWithSize) { | ||
| 113 | + LKLog.w { "unknown CameraCapturer class: ${targetVideoCapturer.javaClass.canonicalName}. Reported dimensions may be inaccurate." } | ||
| 95 | } | 114 | } |
| 115 | + return Pair( | ||
| 116 | + targetVideoCapturer, | ||
| 117 | + newOptions, | ||
| 118 | + ) | ||
| 119 | + } | ||
| 96 | 120 | ||
| 97 | - if (targetVideoCapturer is Camera2Capturer) { | ||
| 98 | - return Pair( | ||
| 99 | - Camera2CapturerWithSize( | ||
| 100 | - targetVideoCapturer, | ||
| 101 | - context.getSystemService(Context.CAMERA_SERVICE) as CameraManager, | ||
| 102 | - targetDeviceName, | ||
| 103 | - cameraEventsDispatchHandler, | ||
| 104 | - ), | ||
| 105 | - newOptions, | 121 | + private fun createCamera1Provider() = object : CameraProvider { |
| 122 | + private val enumerator by lazy { Camera1Enumerator(true) } | ||
| 123 | + | ||
| 124 | + override val cameraVersion = 1 | ||
| 125 | + | ||
| 126 | + override fun provideEnumerator(context: Context) = enumerator | ||
| 127 | + | ||
| 128 | + override fun provideCapturer( | ||
| 129 | + context: Context, | ||
| 130 | + options: LocalVideoTrackOptions, | ||
| 131 | + eventsHandler: CameraEventsDispatchHandler, | ||
| 132 | + ): VideoCapturer { | ||
| 133 | + val targetDeviceName = enumerator.findCamera(options.deviceId, options.position) | ||
| 134 | + // Cache supported capture formats ahead of time to avoid future camera locks. | ||
| 135 | + Camera1Helper.getSupportedFormats(Camera1Helper.getCameraId(targetDeviceName)) | ||
| 136 | + val targetVideoCapturer = enumerator.createCapturer(targetDeviceName, eventsHandler) | ||
| 137 | + return Camera1CapturerWithSize( | ||
| 138 | + targetVideoCapturer as Camera1Capturer, | ||
| 139 | + targetDeviceName, | ||
| 140 | + eventsHandler, | ||
| 106 | ) | 141 | ) |
| 107 | } | 142 | } |
| 108 | 143 | ||
| 109 | - LKLog.w { "unknown CameraCapturer class: ${targetVideoCapturer.javaClass.canonicalName}. Reported dimensions may be inaccurate." } | ||
| 110 | - if (targetVideoCapturer != null) { | ||
| 111 | - return Pair( | ||
| 112 | - targetVideoCapturer, | ||
| 113 | - newOptions, | 144 | + override fun isSupported(context: Context) = true |
| 145 | + } | ||
| 146 | + | ||
| 147 | + private fun createCamera2Provider() = object : CameraProvider { | ||
| 148 | + private var enumerator: Camera2Enumerator? = null | ||
| 149 | + | ||
| 150 | + override val cameraVersion = 2 | ||
| 151 | + | ||
| 152 | + override fun provideEnumerator(context: Context): CameraEnumerator = | ||
| 153 | + enumerator ?: Camera2Enumerator(context).also { | ||
| 154 | + enumerator = it | ||
| 155 | + } | ||
| 156 | + | ||
| 157 | + override fun provideCapturer( | ||
| 158 | + context: Context, | ||
| 159 | + options: LocalVideoTrackOptions, | ||
| 160 | + eventsHandler: CameraEventsDispatchHandler, | ||
| 161 | + ): VideoCapturer { | ||
| 162 | + val enumerator = provideEnumerator(context) | ||
| 163 | + val targetDeviceName = enumerator.findCamera(options.deviceId, options.position) | ||
| 164 | + val targetVideoCapturer = enumerator.createCapturer(targetDeviceName, eventsHandler) | ||
| 165 | + return Camera2CapturerWithSize( | ||
| 166 | + targetVideoCapturer as Camera2Capturer, | ||
| 167 | + context.getSystemService(Context.CAMERA_SERVICE) as CameraManager, | ||
| 168 | + targetDeviceName, | ||
| 169 | + eventsHandler, | ||
| 114 | ) | 170 | ) |
| 115 | } | 171 | } |
| 116 | 172 | ||
| 117 | - return null | 173 | + override fun isSupported(context: Context) = Camera2Enumerator.isSupported(context) |
| 118 | } | 174 | } |
| 119 | 175 | ||
| 120 | /** | 176 | /** |
| @@ -180,4 +236,11 @@ object CameraCapturerUtils { | @@ -180,4 +236,11 @@ object CameraCapturerUtils { | ||
| 180 | } | 236 | } |
| 181 | return null | 237 | return null |
| 182 | } | 238 | } |
| 239 | + | ||
| 240 | + interface CameraProvider { | ||
| 241 | + val cameraVersion: Int | ||
| 242 | + fun provideEnumerator(context: Context): CameraEnumerator | ||
| 243 | + fun provideCapturer(context: Context, options: LocalVideoTrackOptions, eventsHandler: CameraEventsDispatchHandler): VideoCapturer | ||
| 244 | + fun isSupported(context: Context): Boolean | ||
| 245 | + } | ||
| 183 | } | 246 | } |
livekit-android-sdk/src/main/java/io/livekit/android/room/track/video/CameraEventsDispatchHandler.kt
| @@ -20,10 +20,8 @@ import livekit.org.webrtc.CameraVideoCapturer.CameraEventsHandler | @@ -20,10 +20,8 @@ import livekit.org.webrtc.CameraVideoCapturer.CameraEventsHandler | ||
| 20 | 20 | ||
| 21 | /** | 21 | /** |
| 22 | * Dispatches CameraEventsHandler callbacks to registered handlers. | 22 | * Dispatches CameraEventsHandler callbacks to registered handlers. |
| 23 | - * | ||
| 24 | - * @suppress | ||
| 25 | */ | 23 | */ |
| 26 | -internal class CameraEventsDispatchHandler : CameraEventsHandler { | 24 | +class CameraEventsDispatchHandler : CameraEventsHandler { |
| 27 | private val handlers = mutableSetOf<CameraEventsHandler>() | 25 | private val handlers = mutableSetOf<CameraEventsHandler>() |
| 28 | 26 | ||
| 29 | @Synchronized | 27 | @Synchronized |
| @@ -19,18 +19,11 @@ package io.livekit.android.room.track.video | @@ -19,18 +19,11 @@ package io.livekit.android.room.track.video | ||
| 19 | import android.hardware.camera2.CameraManager | 19 | import android.hardware.camera2.CameraManager |
| 20 | import livekit.org.webrtc.* | 20 | import livekit.org.webrtc.* |
| 21 | 21 | ||
| 22 | -/** | ||
| 23 | - * @suppress | ||
| 24 | - */ | ||
| 25 | -internal interface VideoCapturerWithSize : VideoCapturer { | 22 | +interface VideoCapturerWithSize : VideoCapturer { |
| 26 | fun findCaptureFormat(width: Int, height: Int): Size | 23 | fun findCaptureFormat(width: Int, height: Int): Size |
| 27 | } | 24 | } |
| 28 | 25 | ||
| 29 | -/** | ||
| 30 | - * @suppress | ||
| 31 | - */ | ||
| 32 | - | ||
| 33 | -internal abstract class CameraCapturerWithSize( | 26 | +abstract class CameraCapturerWithSize( |
| 34 | val cameraEventsDispatchHandler: CameraEventsDispatchHandler, | 27 | val cameraEventsDispatchHandler: CameraEventsDispatchHandler, |
| 35 | ) : VideoCapturerWithSize | 28 | ) : VideoCapturerWithSize |
| 36 | 29 |
-
请 注册 或 登录 后发表评论