davidliu
Committed by GitHub

Fix race condition with videoTrack.addRenderer (#448)

@@ -52,6 +52,16 @@ abstract class Track( @@ -52,6 +52,16 @@ abstract class Track(
52 var statsGetter: RTCStatsGetter? = null 52 var statsGetter: RTCStatsGetter? = null
53 53
54 /** 54 /**
  55 + * [MediaStreamTrack] doesn't expose a way to tell if it's disposed. Track it here.
  56 + * This can only be safely accessed on the rtc thread.
  57 + *
  58 + * Note: [MediaStreamTrack] can still be disposed if we don't own it, so this isn't necessarily safe,
  59 + * however generally all the tracks passed into this class are owned by this class.
  60 + */
  61 + internal var isDisposed = false
  62 + private set
  63 +
  64 + /**
55 * Return the [RTCStatsReport] for this track, or null if none is available. 65 * Return the [RTCStatsReport] for this track, or null if none is available.
56 */ 66 */
57 suspend fun getRTCStats(): RTCStatsReport? = statsGetter?.getStats() 67 suspend fun getRTCStats(): RTCStatsReport? = statsGetter?.getStats()
@@ -172,6 +182,7 @@ abstract class Track( @@ -172,6 +182,7 @@ abstract class Track(
172 */ 182 */
173 open fun dispose() { 183 open fun dispose() {
174 executeBlockingOnRTCThread { 184 executeBlockingOnRTCThread {
  185 + isDisposed = true
175 rtcTrack.dispose() 186 rtcTrack.dispose()
176 } 187 }
177 } 188 }
@@ -35,26 +35,32 @@ abstract class VideoTrack(name: String, override val rtcTrack: VideoTrack) : @@ -35,26 +35,32 @@ abstract class VideoTrack(name: String, override val rtcTrack: VideoTrack) :
35 */ 35 */
36 open fun addRenderer(renderer: VideoSink) { 36 open fun addRenderer(renderer: VideoSink) {
37 executeBlockingOnRTCThread { 37 executeBlockingOnRTCThread {
  38 + if (!isDisposed) {
38 sinks.add(renderer) 39 sinks.add(renderer)
39 rtcTrack.addSink(renderer) 40 rtcTrack.addSink(renderer)
40 } 41 }
41 } 42 }
  43 + }
42 44
43 /** 45 /**
44 * Remove a previously added [VideoSink]. 46 * Remove a previously added [VideoSink].
45 */ 47 */
46 open fun removeRenderer(renderer: VideoSink) { 48 open fun removeRenderer(renderer: VideoSink) {
47 executeBlockingOnRTCThread { 49 executeBlockingOnRTCThread {
  50 + if (!isDisposed) {
48 rtcTrack.removeSink(renderer) 51 rtcTrack.removeSink(renderer)
49 sinks.remove(renderer) 52 sinks.remove(renderer)
50 } 53 }
51 } 54 }
  55 + }
52 56
53 override fun stop() { 57 override fun stop() {
54 executeBlockingOnRTCThread { 58 executeBlockingOnRTCThread {
  59 + if (!isDisposed) {
55 for (sink in sinks) { 60 for (sink in sinks) {
56 rtcTrack.removeSink(sink) 61 rtcTrack.removeSink(sink)
57 } 62 }
  63 + }
58 sinks.clear() 64 sinks.clear()
59 } 65 }
60 super.stop() 66 super.stop()