davidliu
Committed by GitHub

More guarding of rtc api usages (#488)

* More guarding of rtc api usages

* spotless

* changeset
  1 +---
  2 +"client-sdk-android": patch
  3 +---
  4 +
  5 +More guarding of rtc api usages to prevent crashes
@@ -34,7 +34,17 @@ import io.livekit.android.util.LoggingLevel @@ -34,7 +34,17 @@ import io.livekit.android.util.LoggingLevel
34 import io.livekit.android.webrtc.CustomAudioProcessingFactory 34 import io.livekit.android.webrtc.CustomAudioProcessingFactory
35 import io.livekit.android.webrtc.CustomVideoDecoderFactory 35 import io.livekit.android.webrtc.CustomVideoDecoderFactory
36 import io.livekit.android.webrtc.CustomVideoEncoderFactory 36 import io.livekit.android.webrtc.CustomVideoEncoderFactory
37 -import livekit.org.webrtc.* 37 +import io.livekit.android.webrtc.peerconnection.executeBlockingOnRTCThread
  38 +import livekit.org.webrtc.AudioProcessingFactory
  39 +import livekit.org.webrtc.EglBase
  40 +import livekit.org.webrtc.Logging
  41 +import livekit.org.webrtc.MediaStreamTrack
  42 +import livekit.org.webrtc.PeerConnectionFactory
  43 +import livekit.org.webrtc.RtpCapabilities
  44 +import livekit.org.webrtc.SoftwareVideoDecoderFactory
  45 +import livekit.org.webrtc.SoftwareVideoEncoderFactory
  46 +import livekit.org.webrtc.VideoDecoderFactory
  47 +import livekit.org.webrtc.VideoEncoderFactory
38 import livekit.org.webrtc.audio.AudioDeviceModule 48 import livekit.org.webrtc.audio.AudioDeviceModule
39 import livekit.org.webrtc.audio.JavaAudioDeviceModule 49 import livekit.org.webrtc.audio.JavaAudioDeviceModule
40 import timber.log.Timber 50 import timber.log.Timber
@@ -295,7 +305,8 @@ internal object RTCModule { @@ -295,7 +305,8 @@ internal object RTCModule {
295 memoryManager: CloseableManager, 305 memoryManager: CloseableManager,
296 audioProcessingFactory: AudioProcessingFactory, 306 audioProcessingFactory: AudioProcessingFactory,
297 ): PeerConnectionFactory { 307 ): PeerConnectionFactory {
298 - return PeerConnectionFactory.builder() 308 + return executeBlockingOnRTCThread {
  309 + PeerConnectionFactory.builder()
299 .setAudioDeviceModule(audioDeviceModule) 310 .setAudioDeviceModule(audioDeviceModule)
300 .setAudioProcessingFactory(audioProcessingFactory) 311 .setAudioProcessingFactory(audioProcessingFactory)
301 .setVideoEncoderFactory(videoEncoderFactory) 312 .setVideoEncoderFactory(videoEncoderFactory)
@@ -308,6 +319,7 @@ internal object RTCModule { @@ -308,6 +319,7 @@ internal object RTCModule {
308 .createPeerConnectionFactory() 319 .createPeerConnectionFactory()
309 .apply { memoryManager.registerClosable { dispose() } } 320 .apply { memoryManager.registerClosable { dispose() } }
310 } 321 }
  322 + }
311 323
312 @Provides 324 @Provides
313 @Named(InjectionNames.SENDER) 325 @Named(InjectionNames.SENDER)
@@ -23,7 +23,11 @@ import dagger.assisted.Assisted @@ -23,7 +23,11 @@ import dagger.assisted.Assisted
23 import dagger.assisted.AssistedFactory 23 import dagger.assisted.AssistedFactory
24 import dagger.assisted.AssistedInject 24 import dagger.assisted.AssistedInject
25 import io.livekit.android.dagger.InjectionNames 25 import io.livekit.android.dagger.InjectionNames
26 -import io.livekit.android.room.util.* 26 +import io.livekit.android.room.util.MediaConstraintKeys
  27 +import io.livekit.android.room.util.createOffer
  28 +import io.livekit.android.room.util.findConstraint
  29 +import io.livekit.android.room.util.setLocalDescription
  30 +import io.livekit.android.room.util.setRemoteDescription
27 import io.livekit.android.util.Either 31 import io.livekit.android.util.Either
28 import io.livekit.android.util.LKLog 32 import io.livekit.android.util.LKLog
29 import io.livekit.android.util.debounce 33 import io.livekit.android.util.debounce
@@ -40,9 +44,14 @@ import kotlinx.coroutines.CoroutineDispatcher @@ -40,9 +44,14 @@ import kotlinx.coroutines.CoroutineDispatcher
40 import kotlinx.coroutines.CoroutineScope 44 import kotlinx.coroutines.CoroutineScope
41 import kotlinx.coroutines.SupervisorJob 45 import kotlinx.coroutines.SupervisorJob
42 import kotlinx.coroutines.runBlocking 46 import kotlinx.coroutines.runBlocking
43 -import livekit.org.webrtc.* 47 +import livekit.org.webrtc.IceCandidate
  48 +import livekit.org.webrtc.MediaConstraints
  49 +import livekit.org.webrtc.PeerConnection
44 import livekit.org.webrtc.PeerConnection.RTCConfiguration 50 import livekit.org.webrtc.PeerConnection.RTCConfiguration
45 import livekit.org.webrtc.PeerConnection.SignalingState 51 import livekit.org.webrtc.PeerConnection.SignalingState
  52 +import livekit.org.webrtc.PeerConnectionFactory
  53 +import livekit.org.webrtc.RtpTransceiver
  54 +import livekit.org.webrtc.SessionDescription
46 import java.util.concurrent.atomic.AtomicBoolean 55 import java.util.concurrent.atomic.AtomicBoolean
47 import javax.inject.Named 56 import javax.inject.Named
48 import kotlin.contracts.ExperimentalContracts 57 import kotlin.contracts.ExperimentalContracts
@@ -67,10 +76,12 @@ constructor( @@ -67,10 +76,12 @@ constructor(
67 private val coroutineScope = CoroutineScope(ioDispatcher + SupervisorJob()) 76 private val coroutineScope = CoroutineScope(ioDispatcher + SupervisorJob())
68 77
69 @VisibleForTesting 78 @VisibleForTesting
70 - internal val peerConnection: PeerConnection = connectionFactory.createPeerConnection( 79 + internal val peerConnection: PeerConnection = executeBlockingOnRTCThread {
  80 + connectionFactory.createPeerConnection(
71 config, 81 config,
72 pcObserver, 82 pcObserver,
73 ) ?: throw IllegalStateException("peer connection creation failed?") 83 ) ?: throw IllegalStateException("peer connection creation failed?")
  84 + }
74 private val pendingCandidates = mutableListOf<IceCandidate>() 85 private val pendingCandidates = mutableListOf<IceCandidate>()
75 private var restartingIce: Boolean = false 86 private var restartingIce: Boolean = false
76 87
@@ -18,6 +18,8 @@ package io.livekit.android.room.participant @@ -18,6 +18,8 @@ package io.livekit.android.room.participant
18 18
19 import io.livekit.android.events.ParticipantEvent 19 import io.livekit.android.events.ParticipantEvent
20 import io.livekit.android.room.SignalClient 20 import io.livekit.android.room.SignalClient
  21 +import io.livekit.android.room.track.KIND_AUDIO
  22 +import io.livekit.android.room.track.KIND_VIDEO
21 import io.livekit.android.room.track.RemoteAudioTrack 23 import io.livekit.android.room.track.RemoteAudioTrack
22 import io.livekit.android.room.track.RemoteTrackPublication 24 import io.livekit.android.room.track.RemoteTrackPublication
23 import io.livekit.android.room.track.RemoteVideoTrack 25 import io.livekit.android.room.track.RemoteVideoTrack
@@ -215,9 +217,4 @@ class RemoteParticipant( @@ -215,9 +217,4 @@ class RemoteParticipant(
215 internal fun onDataReceived(data: ByteArray, topic: String?) { 217 internal fun onDataReceived(data: ByteArray, topic: String?) {
216 eventBus.postEvent(ParticipantEvent.DataReceived(this, data, topic), scope) 218 eventBus.postEvent(ParticipantEvent.DataReceived(this, data, topic), scope)
217 } 219 }
218 -  
219 - companion object {  
220 - private const val KIND_AUDIO = "audio"  
221 - private const val KIND_VIDEO = "video"  
222 - }  
223 } 220 }
@@ -29,7 +29,6 @@ import io.livekit.android.room.participant.LocalParticipant @@ -29,7 +29,6 @@ import io.livekit.android.room.participant.LocalParticipant
29 import io.livekit.android.util.FlowObservable 29 import io.livekit.android.util.FlowObservable
30 import io.livekit.android.util.flow 30 import io.livekit.android.util.flow
31 import io.livekit.android.util.flowDelegate 31 import io.livekit.android.util.flowDelegate
32 -import io.livekit.android.webrtc.peerconnection.executeBlockingOnRTCThread  
33 import kotlinx.coroutines.CoroutineDispatcher 32 import kotlinx.coroutines.CoroutineDispatcher
34 import kotlinx.coroutines.CoroutineScope 33 import kotlinx.coroutines.CoroutineScope
35 import kotlinx.coroutines.SupervisorJob 34 import kotlinx.coroutines.SupervisorJob
@@ -64,11 +63,6 @@ constructor( @@ -64,11 +63,6 @@ constructor(
64 * To only be used for flow delegate scoping, and should not be cancelled. 63 * To only be used for flow delegate scoping, and should not be cancelled.
65 **/ 64 **/
66 private val delegateScope = CoroutineScope(dispatcher + SupervisorJob()) 65 private val delegateScope = CoroutineScope(dispatcher + SupervisorJob())
67 - var enabled: Boolean  
68 - get() = executeBlockingOnRTCThread { rtcTrack.enabled() }  
69 - set(value) {  
70 - executeBlockingOnRTCThread { rtcTrack.setEnabled(value) }  
71 - }  
72 66
73 internal var transceiver: RtpTransceiver? = null 67 internal var transceiver: RtpTransceiver? = null
74 internal val sender: RtpSender? 68 internal val sender: RtpSender?
@@ -40,7 +40,7 @@ class LocalTrackPublication( @@ -40,7 +40,7 @@ class LocalTrackPublication(
40 40
41 val mediaTrack = track ?: return 41 val mediaTrack = track ?: return
42 42
43 - mediaTrack.rtcTrack.setEnabled(!muted) 43 + mediaTrack.enabled = !muted
44 super.muted = muted 44 super.muted = muted
45 45
46 // send updates to server 46 // send updates to server
@@ -254,6 +254,11 @@ constructor( @@ -254,6 +254,11 @@ constructor(
254 * Restart a track with new options. 254 * Restart a track with new options.
255 */ 255 */
256 fun restartTrack(options: LocalVideoTrackOptions = defaultsManager.videoTrackCaptureDefaults.copy()) { 256 fun restartTrack(options: LocalVideoTrackOptions = defaultsManager.videoTrackCaptureDefaults.copy()) {
  257 + if (isDisposed) {
  258 + LKLog.e { "Attempting to restart track that was already disposed, aborting." }
  259 + return
  260 + }
  261 +
257 val oldCapturer = capturer 262 val oldCapturer = capturer
258 val oldSource = source 263 val oldSource = source
259 val oldRtcTrack = rtcTrack 264 val oldRtcTrack = rtcTrack
@@ -40,18 +40,22 @@ class RemoteAudioTrack( @@ -40,18 +40,22 @@ class RemoteAudioTrack(
40 */ 40 */
41 fun addSink(sink: AudioTrackSink) { 41 fun addSink(sink: AudioTrackSink) {
42 executeBlockingOnRTCThread { 42 executeBlockingOnRTCThread {
  43 + if (!isDisposed) {
43 rtcTrack.addSink(sink) 44 rtcTrack.addSink(sink)
44 } 45 }
45 } 46 }
  47 + }
46 48
47 /** 49 /**
48 * Removes a previously added sink. 50 * Removes a previously added sink.
49 */ 51 */
50 fun removeSink(sink: AudioTrackSink) { 52 fun removeSink(sink: AudioTrackSink) {
51 executeBlockingOnRTCThread { 53 executeBlockingOnRTCThread {
  54 + if (!isDisposed) {
52 rtcTrack.removeSink(sink) 55 rtcTrack.removeSink(sink)
53 } 56 }
54 } 57 }
  58 + }
55 59
56 /** 60 /**
57 * Sets the volume. 61 * Sets the volume.
@@ -63,7 +67,9 @@ class RemoteAudioTrack( @@ -63,7 +67,9 @@ class RemoteAudioTrack(
63 */ 67 */
64 fun setVolume(volume: Double) { 68 fun setVolume(volume: Double) {
65 executeBlockingOnRTCThread { 69 executeBlockingOnRTCThread {
  70 + if (!isDisposed) {
66 rtcTrack.setVolume(volume) 71 rtcTrack.setVolume(volume)
67 } 72 }
68 } 73 }
  74 + }
69 } 75 }
@@ -49,6 +49,22 @@ abstract class Track( @@ -49,6 +49,22 @@ abstract class Track(
49 } 49 }
50 internal set 50 internal set
51 51
  52 + var enabled: Boolean
  53 + get() = executeBlockingOnRTCThread {
  54 + if (!isDisposed) {
  55 + rtcTrack.enabled()
  56 + } else {
  57 + false
  58 + }
  59 + }
  60 + set(value) {
  61 + executeBlockingOnRTCThread {
  62 + if (!isDisposed) {
  63 + rtcTrack.setEnabled(value)
  64 + }
  65 + }
  66 + }
  67 +
52 var statsGetter: RTCStatsGetter? = null 68 var statsGetter: RTCStatsGetter? = null
53 69
54 /** 70 /**
@@ -163,18 +179,14 @@ abstract class Track( @@ -163,18 +179,14 @@ abstract class Track(
163 * Starts the track. 179 * Starts the track.
164 */ 180 */
165 open fun start() { 181 open fun start() {
166 - executeBlockingOnRTCThread {  
167 - rtcTrack.setEnabled(true)  
168 - } 182 + enabled = true
169 } 183 }
170 184
171 /** 185 /**
172 * Stops the track. 186 * Stops the track.
173 */ 187 */
174 open fun stop() { 188 open fun stop() {
175 - executeBlockingOnRTCThread {  
176 - rtcTrack.setEnabled(false)  
177 - } 189 + enabled = false
178 } 190 }
179 191
180 /** 192 /**
@@ -205,3 +217,6 @@ sealed class TrackException(message: String? = null, cause: Throwable? = null) : @@ -205,3 +217,6 @@ sealed class TrackException(message: String? = null, cause: Throwable? = null) :
205 class PublishException(message: String? = null, cause: Throwable? = null) : 217 class PublishException(message: String? = null, cause: Throwable? = null) :
206 TrackException(message, cause) 218 TrackException(message, cause)
207 } 219 }
  220 +
  221 +public const val KIND_AUDIO = "audio"
  222 +public const val KIND_VIDEO = "video"
@@ -24,12 +24,6 @@ abstract class VideoTrack(name: String, override val rtcTrack: VideoTrack) : @@ -24,12 +24,6 @@ abstract class VideoTrack(name: String, override val rtcTrack: VideoTrack) :
24 Track(name, Kind.VIDEO, rtcTrack) { 24 Track(name, Kind.VIDEO, rtcTrack) {
25 protected val sinks: MutableList<VideoSink> = ArrayList() 25 protected val sinks: MutableList<VideoSink> = ArrayList()
26 26
27 - var enabled: Boolean  
28 - get() = rtcTrack.enabled()  
29 - set(value) {  
30 - rtcTrack.setEnabled(value)  
31 - }  
32 -  
33 /** 27 /**
34 * Add a [VideoSink] that will receive frames. 28 * Add a [VideoSink] that will receive frames.
35 */ 29 */