davidliu
Committed by GitHub

More fixes for crashes caused by using disposed track (#497)

* More fixes for crashes caused by using disposed track

* Add withRTCTrack method to refactor the isDisposed usage

* spotless
---
"client-sdk-android": patch
---
More fixes for crashes caused by using disposed track
... ...
[versions]
webrtc = "125.6422.04"
webrtc = "125.6422.05"
androidJainSipRi = "1.3.0-91"
androidx-activity = "1.9.0"
... ...
... ... @@ -16,7 +16,6 @@
package io.livekit.android.room.track
import io.livekit.android.webrtc.peerconnection.executeBlockingOnRTCThread
import livekit.org.webrtc.AudioTrack
import livekit.org.webrtc.AudioTrackSink
import livekit.org.webrtc.RtpReceiver
... ... @@ -39,23 +38,19 @@ class RemoteAudioTrack(
* to use the data after this function returns.
*/
fun addSink(sink: AudioTrackSink) {
executeBlockingOnRTCThread {
if (!isDisposed) {
withRTCTrack {
rtcTrack.addSink(sink)
}
}
}
/**
* Removes a previously added sink.
*/
fun removeSink(sink: AudioTrackSink) {
executeBlockingOnRTCThread {
if (!isDisposed) {
withRTCTrack {
rtcTrack.removeSink(sink)
}
}
}
/**
* Sets the volume.
... ... @@ -66,10 +61,8 @@ class RemoteAudioTrack(
* * values greater than 1 will boost the volume of the track.
*/
fun setVolume(volume: Double) {
executeBlockingOnRTCThread {
if (!isDisposed) {
withRTCTrack {
rtcTrack.setVolume(volume)
}
}
}
}
... ...
... ... @@ -27,6 +27,9 @@ import livekit.LivekitRtc
import livekit.org.webrtc.MediaStreamTrack
import livekit.org.webrtc.RTCStatsCollectorCallback
import livekit.org.webrtc.RTCStatsReport
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
abstract class Track(
name: String,
... ... @@ -50,32 +53,13 @@ abstract class Track(
internal set
var enabled: Boolean
get() = executeBlockingOnRTCThread {
if (!isDisposed) {
rtcTrack.enabled()
} else {
false
}
}
set(value) {
executeBlockingOnRTCThread {
if (!isDisposed) {
rtcTrack.setEnabled(value)
}
}
}
get() = withRTCTrack(defaultValue = false) { rtcTrack.enabled() }
set(value) = withRTCTrack { rtcTrack.setEnabled(value) }
var statsGetter: RTCStatsGetter? = null
/**
* [MediaStreamTrack] doesn't expose a way to tell if it's disposed. Track it here.
* This can only be safely accessed on the rtc thread.
*
* Note: [MediaStreamTrack] can still be disposed if we don't own it, so this isn't necessarily safe,
* however generally all the tracks passed into this class are owned by this class.
*/
internal var isDisposed = false
private set
internal val isDisposed
get() = rtcTrack.isDisposed
/**
* Return the [RTCStatsReport] for this track, or null if none is available.
... ... @@ -193,11 +177,31 @@ abstract class Track(
* Disposes the track. LiveKit will generally take care of disposing tracks for you.
*/
open fun dispose() {
executeBlockingOnRTCThread {
isDisposed = true
withRTCTrack {
rtcTrack.dispose()
}
}
@OptIn(ExperimentalContracts::class)
internal inline fun <T> withRTCTrack(crossinline action: MediaStreamTrack.() -> T) {
contract { callsInPlace(action, InvocationKind.AT_MOST_ONCE) }
withRTCTrack(Unit, action)
}
@OptIn(ExperimentalContracts::class)
internal inline fun <T> withRTCTrack(defaultValue: T, crossinline action: MediaStreamTrack.() -> T): T {
contract { callsInPlace(action, InvocationKind.AT_MOST_ONCE) }
if (isDisposed) {
return defaultValue
}
return executeBlockingOnRTCThread {
return@executeBlockingOnRTCThread if (isDisposed) {
defaultValue
} else {
action(rtcTrack)
}
}
}
}
sealed class TrackException(message: String? = null, cause: Throwable? = null) :
... ...
... ... @@ -28,13 +28,11 @@ abstract class VideoTrack(name: String, override val rtcTrack: VideoTrack) :
* Add a [VideoSink] that will receive frames.
*/
open fun addRenderer(renderer: VideoSink) {
executeBlockingOnRTCThread {
if (!isDisposed) {
withRTCTrack {
sinks.add(renderer)
rtcTrack.addSink(renderer)
}
}
}
/**
* Remove a previously added [VideoSink].
... ... @@ -43,8 +41,8 @@ abstract class VideoTrack(name: String, override val rtcTrack: VideoTrack) :
executeBlockingOnRTCThread {
if (!isDisposed) {
rtcTrack.removeSink(renderer)
sinks.remove(renderer)
}
sinks.remove(renderer)
}
}
... ...
... ... @@ -47,4 +47,8 @@ class MockMediaStreamTrack(
}
disposed = true
}
override fun isDisposed(): Boolean {
return disposed
}
}
... ...