davidliu
Committed by GitHub

Fix memory leaks (#306)

* Fix memory leaks

* Fix data channel leaking RTCEngine

* Add room to leak canary in sample app

* remove leakcanary watching of room
@@ -91,6 +91,7 @@ object RTCModule { @@ -91,6 +91,7 @@ object RTCModule {
91 moduleCustomizer: ((builder: JavaAudioDeviceModule.Builder) -> Unit)?, 91 moduleCustomizer: ((builder: JavaAudioDeviceModule.Builder) -> Unit)?,
92 audioOutputAttributes: AudioAttributes, 92 audioOutputAttributes: AudioAttributes,
93 appContext: Context, 93 appContext: Context,
  94 + closeableManager: CloseableManager,
94 ): AudioDeviceModule { 95 ): AudioDeviceModule {
95 if (audioDeviceModuleOverride != null) { 96 if (audioDeviceModuleOverride != null) {
96 return audioDeviceModuleOverride 97 return audioDeviceModuleOverride
@@ -167,12 +168,13 @@ object RTCModule { @@ -167,12 +168,13 @@ object RTCModule {
167 168
168 moduleCustomizer?.invoke(builder) 169 moduleCustomizer?.invoke(builder)
169 return builder.createAudioDeviceModule() 170 return builder.createAudioDeviceModule()
  171 + .apply { closeableManager.registerClosable { release() } }
170 } 172 }
171 173
172 @Provides 174 @Provides
173 @Singleton 175 @Singleton
174 fun eglBase( 176 fun eglBase(
175 - @Singleton memoryManager: CloseableManager, 177 + memoryManager: CloseableManager,
176 ): EglBase { 178 ): EglBase {
177 val eglBase = EglBase.create() 179 val eglBase = EglBase.create()
178 memoryManager.registerResource(eglBase) { eglBase.release() } 180 memoryManager.registerResource(eglBase) { eglBase.release() }
@@ -234,12 +236,14 @@ object RTCModule { @@ -234,12 +236,14 @@ object RTCModule {
234 audioDeviceModule: AudioDeviceModule, 236 audioDeviceModule: AudioDeviceModule,
235 videoEncoderFactory: VideoEncoderFactory, 237 videoEncoderFactory: VideoEncoderFactory,
236 videoDecoderFactory: VideoDecoderFactory, 238 videoDecoderFactory: VideoDecoderFactory,
  239 + memoryManager: CloseableManager,
237 ): PeerConnectionFactory { 240 ): PeerConnectionFactory {
238 return PeerConnectionFactory.builder() 241 return PeerConnectionFactory.builder()
239 .setAudioDeviceModule(audioDeviceModule) 242 .setAudioDeviceModule(audioDeviceModule)
240 .setVideoEncoderFactory(videoEncoderFactory) 243 .setVideoEncoderFactory(videoEncoderFactory)
241 .setVideoDecoderFactory(videoDecoderFactory) 244 .setVideoDecoderFactory(videoDecoderFactory)
242 .createPeerConnectionFactory() 245 .createPeerConnectionFactory()
  246 + .apply { memoryManager.registerClosable { dispose() } }
243 } 247 }
244 248
245 @Provides 249 @Provides
@@ -25,7 +25,6 @@ import io.livekit.android.stats.AndroidNetworkInfo @@ -25,7 +25,6 @@ import io.livekit.android.stats.AndroidNetworkInfo
25 import io.livekit.android.stats.NetworkInfo 25 import io.livekit.android.stats.NetworkInfo
26 import okhttp3.OkHttpClient 26 import okhttp3.OkHttpClient
27 import okhttp3.WebSocket 27 import okhttp3.WebSocket
28 -import java.util.concurrent.TimeUnit  
29 import javax.inject.Named 28 import javax.inject.Named
30 import javax.inject.Singleton 29 import javax.inject.Singleton
31 30
@@ -41,10 +40,6 @@ object WebModule { @@ -41,10 +40,6 @@ object WebModule {
41 @Nullable 40 @Nullable
42 okHttpClientOverride: OkHttpClient?, 41 okHttpClientOverride: OkHttpClient?,
43 ): OkHttpClient { 42 ): OkHttpClient {
44 - OkHttpClient.Builder()  
45 - .pingInterval(20, TimeUnit.SECONDS)  
46 - .build()  
47 -  
48 return okHttpClientOverride ?: OkHttpClient() 43 return okHttpClientOverride ?: OkHttpClient()
49 } 44 }
50 45
@@ -149,7 +149,7 @@ constructor( @@ -149,7 +149,7 @@ constructor(
149 } 149 }
150 150
151 fun close() { 151 fun close() {
152 - peerConnection.close() 152 + peerConnection.dispose()
153 } 153 }
154 154
155 fun updateRTCConfig(config: RTCConfiguration) { 155 fun updateRTCConfig(config: RTCConfiguration) {
@@ -314,13 +314,19 @@ internal constructor( @@ -314,13 +314,19 @@ internal constructor(
314 _publisher = null 314 _publisher = null
315 _subscriber?.close() 315 _subscriber?.close()
316 _subscriber = null 316 _subscriber = null
317 - reliableDataChannel?.close() 317 +
  318 + fun DataChannel?.completeDispose() {
  319 + this?.unregisterObserver()
  320 + this?.close()
  321 + this?.dispose()
  322 + }
  323 + reliableDataChannel?.completeDispose()
318 reliableDataChannel = null 324 reliableDataChannel = null
319 - reliableDataChannelSub?.close() 325 + reliableDataChannelSub?.completeDispose()
320 reliableDataChannelSub = null 326 reliableDataChannelSub = null
321 - lossyDataChannel?.close() 327 + lossyDataChannel?.completeDispose()
322 lossyDataChannel = null 328 lossyDataChannel = null
323 - lossyDataChannelSub?.close() 329 + lossyDataChannelSub?.completeDispose()
324 lossyDataChannelSub = null 330 lossyDataChannelSub = null
325 isSubscriberPrimary = false 331 isSubscriberPrimary = false
326 client.close(reason = reason) 332 client.close(reason = reason)
@@ -49,7 +49,6 @@ import livekit.LivekitModels @@ -49,7 +49,6 @@ import livekit.LivekitModels
49 import livekit.LivekitRtc 49 import livekit.LivekitRtc
50 import org.webrtc.* 50 import org.webrtc.*
51 import javax.inject.Named 51 import javax.inject.Named
52 -import javax.inject.Singleton  
53 52
54 class Room 53 class Room
55 @AssistedInject 54 @AssistedInject
@@ -64,7 +63,6 @@ constructor( @@ -64,7 +63,6 @@ constructor(
64 @Named(InjectionNames.DISPATCHER_IO) 63 @Named(InjectionNames.DISPATCHER_IO)
65 private val ioDispatcher: CoroutineDispatcher, 64 private val ioDispatcher: CoroutineDispatcher,
66 val audioHandler: AudioHandler, 65 val audioHandler: AudioHandler,
67 - @Singleton  
68 private val closeableManager: CloseableManager, 66 private val closeableManager: CloseableManager,
69 private val e2EEManagerFactory: E2EEManager.Factory, 67 private val e2EEManagerFactory: E2EEManager.Factory,
70 ) : RTCEngine.Listener, ParticipantListener { 68 ) : RTCEngine.Listener, ParticipantListener {
@@ -127,7 +127,7 @@ class CallViewModel( @@ -127,7 +127,7 @@ class CallViewModel(
127 handlePrimarySpeaker( 127 handlePrimarySpeaker(
128 participantsList, 128 participantsList,
129 speakers, 129 speakers,
130 - room 130 + room,
131 ) 131 )
132 } 132 }
133 } 133 }
@@ -142,6 +142,7 @@ class CallViewModel( @@ -142,6 +142,7 @@ class CallViewModel(
142 messagesReceived++ 142 messagesReceived++
143 Timber.e { "message received from $identity, count $messagesReceived" } 143 Timber.e { "message received from $identity, count $messagesReceived" }
144 } 144 }
  145 +
145 else -> { 146 else -> {
146 Timber.e { "Room event: $it" } 147 Timber.e { "Room event: $it" }
147 } 148 }
@@ -182,7 +183,7 @@ class CallViewModel( @@ -182,7 +183,7 @@ class CallViewModel(
182 room.connect( 183 room.connect(
183 url = url, 184 url = url,
184 token = token, 185 token = token,
185 - roomOptions = RoomOptions(e2eeOptions = getE2EEOptions()) 186 + roomOptions = RoomOptions(e2eeOptions = getE2EEOptions()),
186 ) 187 )
187 188
188 // Create and publish audio/video tracks 189 // Create and publish audio/video tracks
@@ -246,7 +247,7 @@ class CallViewModel( @@ -246,7 +247,7 @@ class CallViewModel(
246 val screencastTrack = 247 val screencastTrack =
247 localParticipant.createScreencastTrack(mediaProjectionPermissionResultData = mediaProjectionPermissionResultData) 248 localParticipant.createScreencastTrack(mediaProjectionPermissionResultData = mediaProjectionPermissionResultData)
248 localParticipant.publishVideoTrack( 249 localParticipant.publishVideoTrack(
249 - screencastTrack 250 + screencastTrack,
250 ) 251 )
251 252
252 // Must start the foreground prior to startCapture. 253 // Must start the foreground prior to startCapture.
@@ -270,6 +271,8 @@ class CallViewModel( @@ -270,6 +271,8 @@ class CallViewModel(
270 271
271 override fun onCleared() { 272 override fun onCleared() {
272 super.onCleared() 273 super.onCleared()
  274 +
  275 + // Make sure to release any resources associated with LiveKit
273 room.disconnect() 276 room.disconnect()
274 room.release() 277 room.release()
275 278