jfilo-ebay
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>
@@ -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 }
@@ -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